Merge pull request #2275 from fonttools/subset-device

[subset] Fix hint-dropping
This commit is contained in:
Behdad Esfahbod 2021-04-22 12:37:48 -06:00 committed by GitHub
commit 9959916c64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 58 additions and 13 deletions

View File

@ -14,6 +14,7 @@ import struct
import array
import logging
from collections import Counter, defaultdict
from functools import reduce
from types import MethodType
__usage__ = "pyftsubset font-file [glyph...] [--option=value]..."
@ -527,6 +528,17 @@ def subset_glyphs(self, s):
else:
assert 0, "unknown format: %s" % self.Format
@_add_method(otTables.Device)
def is_hinting(self):
return self.DeltaFormat in (1,2,3)
@_add_method(otTables.ValueRecord)
def prune_hints(self):
for name in ['XPlaDevice', 'YPlaDevice', 'XAdvDevice', 'YAdvDevice']:
v = getattr(self, name, None)
if v is not None and v.is_hinting():
delattr(self, name)
@_add_method(otTables.SinglePos)
def subset_glyphs(self, s):
if self.Format == 1:
@ -543,15 +555,24 @@ def subset_glyphs(self, s):
@_add_method(otTables.SinglePos)
def prune_post_subset(self, font, options):
if not options.hinting:
# Drop device tables
self.ValueFormat &= ~0x00F0
# Shrink ValueFormat
if self.Format == 1:
if not options.hinting:
self.Value.prune_hints()
self.ValueFormat = self.Value.getEffectiveFormat()
elif self.Format == 2:
if not options.hinting:
for v in self.Value:
v.prune_hints()
self.ValueFormat = reduce(int.__or__, [v.getEffectiveFormat() for v in self.Value], 0)
# Downgrade to Format 1 if all ValueRecords are the same
if self.Format == 2 and all(v == self.Value[0] for v in self.Value):
self.Format = 1
self.Value = self.Value[0] if self.ValueFormat != 0 else None
del self.ValueCount
return True
return bool(self.ValueFormat)
@_add_method(otTables.PairPos)
def subset_glyphs(self, s):
@ -587,10 +608,22 @@ def subset_glyphs(self, s):
@_add_method(otTables.PairPos)
def prune_post_subset(self, font, options):
if not options.hinting:
# Drop device tables
self.ValueFormat1 &= ~0x00F0
self.ValueFormat2 &= ~0x00F0
return True
attr1, attr2 = {
1: ('PairSet', 'PairValueRecord'),
2: ('Class1Record', 'Class2Record'),
}[self.Format]
self.ValueFormat1 = self.ValueFormat2 = 0
for row in getattr(self, attr1):
for r in getattr(row, attr2):
if r.Value1:
r.Value1.prune_hints()
self.ValueFormat1 |= r.Value1.getEffectiveFormat()
if r.Value2:
r.Value2.prune_hints()
self.ValueFormat2 |= r.Value2.getEffectiveFormat()
return bool(self.ValueFormat1 | self.ValueFormat2)
@_add_method(otTables.CursivePos)
def subset_glyphs(self, s):
@ -606,9 +639,15 @@ def subset_glyphs(self, s):
@_add_method(otTables.Anchor)
def prune_hints(self):
# Drop device tables / contour anchor point
self.ensureDecompiled()
self.Format = 1
if self.Format == 2:
self.Format = 1
elif self.Format == 3:
for name in ('XDeviceTable', 'YDeviceTable'):
v = getattr(self, name, None)
if v is not None and v.is_hinting():
setattr(self, name, None)
if self.XDeviceTable is None and self.YDeviceTable is None:
self.Format = 1
@_add_method(otTables.CursivePos)
def prune_post_subset(self, font, options):
@ -713,7 +752,6 @@ def subset_glyphs(self, s):
@_add_method(otTables.MarkMarkPos)
def prune_post_subset(self, font, options):
if not options.hinting:
# Drop device tables or contour anchor point
for m in self.Mark1Array.MarkRecord:
if m.MarkAnchor:
m.MarkAnchor.prune_hints()

View File

@ -970,6 +970,13 @@ class ValueRecord(object):
format = format | valueRecordFormatDict[name][0]
return format
def getEffectiveFormat(self):
format = 0
for name,value in self.__dict__.items():
if value:
format = format | valueRecordFormatDict[name][0]
return format
def toXML(self, xmlWriter, font, valueName, attrs=None):
if attrs is None:
simpleItems = []

View File

@ -289,7 +289,7 @@ def merge(merger, self, lst):
# Merge everything else; though, there shouldn't be anything else. :)
merger.mergeObjects(self, lst,
exclude=('Format', 'Coverage', 'Value', 'ValueCount'))
self.ValueFormat = reduce(int.__or__, [v.getFormat() for v in self.Value], 0)
self.ValueFormat = reduce(int.__or__, [v.getEffectiveFormat() for v in self.Value], 0)
@AligningMerger.merger(ot.PairSet)
def merge(merger, self, lst):