From 28b179a018cb5c2f4cf9c7ec6dae5febe958f2cc Mon Sep 17 00:00:00 2001 From: Sascha Brawer Date: Tue, 22 Aug 2017 00:55:17 +0200 Subject: [PATCH] [AAT] Implement subsetting of `prop` table with AAT glyph properties --- Lib/fontTools/subset/__init__.py | 21 ++ Tests/subset/data/TestPROP.ttx | 322 ++++++++++++++++++++++++++++ Tests/subset/data/expect_prop_0.ttx | 11 + Tests/subset/data/expect_prop_1.ttx | 14 ++ Tests/subset/subset_test.py | 35 +++ 5 files changed, 403 insertions(+) create mode 100644 Tests/subset/data/TestPROP.ttx create mode 100644 Tests/subset/data/expect_prop_0.ttx create mode 100644 Tests/subset/data/expect_prop_1.ttx diff --git a/Lib/fontTools/subset/__init__.py b/Lib/fontTools/subset/__init__.py index 9b7c3e937..4da48c73a 100644 --- a/Lib/fontTools/subset/__init__.py +++ b/Lib/fontTools/subset/__init__.py @@ -13,6 +13,7 @@ import sys import struct import array import logging +from collections import Counter from types import MethodType __usage__ = "pyftsubset font-file [glyph...] [--option=value]..." @@ -1704,6 +1705,26 @@ def subset_glyphs(self, s): self.extraNames = [] # This seems to do it return True # Required table +@_add_method(ttLib.getTableClass('prop')) +def subset_glyphs(self, s): + prop = self.table.GlyphProperties + if prop.Format == 0: + return prop.DefaultProperties != 0 + elif prop.Format == 1: + prop.Properties = {g: prop.Properties.get(g, prop.DefaultProperties) + for g in s.glyphs} + mostCommon, _cnt = Counter(prop.Properties.values()).most_common(1)[0] + prop.DefaultProperties = mostCommon + prop.Properties = {g: prop for g, prop in prop.Properties.items() + if prop != mostCommon} + if len(prop.Properties) == 0: + del prop.Properties + prop.Format = 0 + return prop.DefaultProperties != 0 + return True + else: + assert False, "unknown 'prop' format %s" % prop.Format + @_add_method(ttLib.getTableClass('COLR')) def closure_glyphs(self, s): decompose = s.glyphs diff --git a/Tests/subset/data/TestPROP.ttx b/Tests/subset/data/TestPROP.ttx new file mode 100644 index 000000000..c783fced0 --- /dev/null +++ b/Tests/subset/data/TestPROP.ttx @@ -0,0 +1,322 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TestPROP + + + Regular + + + TestPROP + + + TestPROP-Regular + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/subset/data/expect_prop_0.ttx b/Tests/subset/data/expect_prop_0.ttx new file mode 100644 index 000000000..f8ca15093 --- /dev/null +++ b/Tests/subset/data/expect_prop_0.ttx @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Tests/subset/data/expect_prop_1.ttx b/Tests/subset/data/expect_prop_1.ttx new file mode 100644 index 000000000..f7f2d23c6 --- /dev/null +++ b/Tests/subset/data/expect_prop_1.ttx @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/Tests/subset/subset_test.py b/Tests/subset/subset_test.py index 938bd8585..0faba5047 100644 --- a/Tests/subset/subset_test.py +++ b/Tests/subset/subset_test.py @@ -123,6 +123,41 @@ class SubsetTest(unittest.TestCase): subsetfont = TTFont(subsetpath) self.expect_ttx(subsetfont, self.getpath("expect_keep_math.ttx"), ["GlyphOrder", "CFF ", "MATH", "hmtx"]) + def test_subset_prop_remove_default_zero(self): + # If all glyphs have an AAT glyph property with value 0, + # the "prop" table should be removed from the subsetted font. + _, fontpath = self.compile_font(self.getpath("TestPROP.ttx"), ".ttf") + subsetpath = self.temp_path(".ttf") + subset.main([fontpath, "--unicodes=U+0041", + "--output-file=%s" % subsetpath]) + subsetfont = TTFont(subsetpath) + self.assertNotIn("prop", subsetfont) + + def test_subset_prop_0(self): + # If all glyphs share the same AAT glyph properties, the "prop" table + # in the subsetted font should use format 0. + # + # Unless the shared value is zero, in which case the subsetted font + # should have no "prop" table at all. But that case has already been + # tested above in test_subset_prop_remove_default_zero(). + _, fontpath = self.compile_font(self.getpath("TestPROP.ttx"), ".ttf") + subsetpath = self.temp_path(".ttf") + subset.main([fontpath, "--unicodes=U+0030-0032", "--no-notdef-glyph", + "--output-file=%s" % subsetpath]) + subsetfont = TTFont(subsetpath) + self.expect_ttx(subsetfont, self.getpath("expect_prop_0.ttx"), ["prop"]) + + def test_subset_prop_1(self): + # If not all glyphs share the same AAT glyph properties, the subsetted + # font should contain a "prop" table in format 1. To save space, the + # DefaultProperties should be set to the most frequent value. + _, fontpath = self.compile_font(self.getpath("TestPROP.ttx"), ".ttf") + subsetpath = self.temp_path(".ttf") + subset.main([fontpath, "--unicodes=U+0030-0032", "--notdef-outline", + "--output-file=%s" % subsetpath]) + subsetfont = TTFont(subsetpath) + self.expect_ttx(subsetfont, self.getpath("expect_prop_1.ttx"), ["prop"]) + def test_options(self): # https://github.com/behdad/fonttools/issues/413 opt1 = subset.Options()