From 33fa149c31bf5b1f7b977b718210c58f27da16e5 Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Wed, 19 Jun 2019 11:31:40 +0100 Subject: [PATCH 1/8] subset: Add tests for --flavor option --- Tests/subset/data/Lobster.subset.otf | Bin 0 -> 3756 bytes Tests/subset/subset_test.py | 42 +++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 Tests/subset/data/Lobster.subset.otf diff --git a/Tests/subset/data/Lobster.subset.otf b/Tests/subset/data/Lobster.subset.otf new file mode 100644 index 0000000000000000000000000000000000000000..8147542729d9636d613f6a5d41d14cb9069854d0 GIT binary patch literal 3756 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 Y-@`W;I&;Gz2tTmY-0k*zd Date: Wed, 19 Jun 2019 11:44:40 +0100 Subject: [PATCH 2/8] woff2: initialize WOFF2FlavorData from existing WOFFFlavorData Fixes #1650 A regression was introduced with 3.43.0 when doing pyftsubset --flavor=woff2 and the input font is WOFF 1.0, thus it has a non-None flavorData already but doesn't define the transformTables attribute. --- Lib/fontTools/ttLib/woff2.py | 51 +++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/Lib/fontTools/ttLib/woff2.py b/Lib/fontTools/ttLib/woff2.py index a8722502e..637cdc67f 100644 --- a/Lib/fontTools/ttLib/woff2.py +++ b/Lib/fontTools/ttLib/woff2.py @@ -174,7 +174,7 @@ class WOFF2Writer(SFNTWriter): self.file = file self.numTables = numTables self.sfntVersion = Tag(sfntVersion) - self.flavorData = flavorData or WOFF2FlavorData() + self.flavorData = WOFF2FlavorData(data=flavorData) self.directoryFormat = woff2DirectoryFormat self.directorySize = woff2DirectorySize @@ -1128,19 +1128,35 @@ class WOFF2FlavorData(WOFFFlavorData): Flavor = 'woff2' - def __init__(self, reader=None, transformedTables=None): + def __init__(self, reader=None, data=None, transformedTables=None): """Data class that holds the WOFF2 header major/minor version, any metadata or private data (as bytes strings), and the set of table tags that have transformations applied (if reader is not None), or will have once the WOFF2 font is compiled. + + Args: + reader: an SFNTReader (or subclass) object to read flavor data from. + data: another WOFFFlavorData object to initialise data from. + transformedTables: set of strings containing table tags to be transformed. + + Raises: + ImportError if the brotli module is not installed. + + NOTE: The 'reader' argument, on the one hand, and the 'data' and + 'transformedTables' arguments, on the other hand, are mutually exclusive. """ if not haveBrotli: raise ImportError("No module named brotli") - if reader is not None and transformedTables is not None: - raise TypeError( - "'reader' and 'transformedTables' arguments are mutually exclusive" - ) + if reader is not None: + if data is not None: + raise TypeError( + "'reader' and 'data' arguments are mutually exclusive" + ) + if transformedTables is not None: + raise TypeError( + "'reader' and 'transformedTables' arguments are mutually exclusive" + ) if transformedTables is None: transformedTables = woff2TransformedTableTags @@ -1164,19 +1180,26 @@ class WOFF2FlavorData(WOFFFlavorData): reader.file.seek(reader.metaOffset) rawData = reader.file.read(reader.metaLength) assert len(rawData) == reader.metaLength - data = brotli.decompress(rawData) - assert len(data) == reader.metaOrigLength - self.metaData = data + metaData = brotli.decompress(rawData) + assert len(mataData) == reader.metaOrigLength + self.metaData = metaData if reader.privLength: reader.file.seek(reader.privOffset) - data = reader.file.read(reader.privLength) - assert len(data) == reader.privLength - self.privData = data + privData = reader.file.read(reader.privLength) + assert len(privData) == reader.privLength + self.privData = privData transformedTables = [ tag for tag, entry in reader.tables.items() if entry.transformed ] + elif data: + self.majorVersion = data.majorVersion + self.majorVersion = data.minorVersion + self.metaData = data.metaData + self.privData = data.privData + if hasattr(data, "transformedTables"): + transformedTables = data.transformedTables self.transformedTables = set(transformedTables) @@ -1354,7 +1377,9 @@ def compress(input_file, output_file, transform_tables=None): font.flavor = "woff2" if transform_tables is not None: - font.flavorData = WOFF2FlavorData(transformedTables=transform_tables) + font.flavorData = WOFF2FlavorData( + data=font.flavorData, transformedTables=transform_tables + ) font.save(output_file, reorderTables=False) From 1c369da8c96e6e9b863777216891cb32b5a520a9 Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Wed, 19 Jun 2019 11:47:32 +0100 Subject: [PATCH 3/8] woff2: fix typo --- Lib/fontTools/ttLib/woff2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/fontTools/ttLib/woff2.py b/Lib/fontTools/ttLib/woff2.py index 637cdc67f..2660348ee 100644 --- a/Lib/fontTools/ttLib/woff2.py +++ b/Lib/fontTools/ttLib/woff2.py @@ -1181,7 +1181,7 @@ class WOFF2FlavorData(WOFFFlavorData): rawData = reader.file.read(reader.metaLength) assert len(rawData) == reader.metaLength metaData = brotli.decompress(rawData) - assert len(mataData) == reader.metaOrigLength + assert len(metaData) == reader.metaOrigLength self.metaData = metaData if reader.privLength: reader.file.seek(reader.privOffset) From 84e9ea167a992012e2cccf84c57a8bfe11ce6a1a Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Wed, 19 Jun 2019 12:13:14 +0100 Subject: [PATCH 4/8] woff2: allow to recompress woff2 while keeping flavorData optionally modifying the list of transformed tables --- Lib/fontTools/ttLib/woff2.py | 10 +++++----- Tests/ttLib/woff2_test.py | 27 ++++++++++++++++++++++++--- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/Lib/fontTools/ttLib/woff2.py b/Lib/fontTools/ttLib/woff2.py index 2660348ee..56b119a7c 100644 --- a/Lib/fontTools/ttLib/woff2.py +++ b/Lib/fontTools/ttLib/woff2.py @@ -1158,10 +1158,7 @@ class WOFF2FlavorData(WOFFFlavorData): "'reader' and 'transformedTables' arguments are mutually exclusive" ) - if transformedTables is None: - transformedTables = woff2TransformedTableTags - else: - if ( + if transformedTables is not None and ( "glyf" in transformedTables and "loca" not in transformedTables or "loca" in transformedTables and "glyf" not in transformedTables ): @@ -1198,9 +1195,12 @@ class WOFF2FlavorData(WOFFFlavorData): self.majorVersion = data.minorVersion self.metaData = data.metaData self.privData = data.privData - if hasattr(data, "transformedTables"): + if transformedTables is None and hasattr(data, "transformedTables"): transformedTables = data.transformedTables + if transformedTables is None: + transformedTables = woff2TransformedTableTags + self.transformedTables = set(transformedTables) diff --git a/Tests/ttLib/woff2_test.py b/Tests/ttLib/woff2_test.py index e4c8bb215..4a474aed7 100644 --- a/Tests/ttLib/woff2_test.py +++ b/Tests/ttLib/woff2_test.py @@ -386,15 +386,18 @@ class WOFF2FlavorDataTest(unittest.TestCase): self.assertEqual(flavorData.minorVersion, 1) def test_mutually_exclusive_args(self): + msg = "arguments are mutually exclusive" reader = DummyReader(self.file) - with self.assertRaisesRegex(TypeError, "arguments are mutually exclusive"): + with self.assertRaisesRegex(TypeError, msg): WOFF2FlavorData(reader, transformedTables={"hmtx"}) + with self.assertRaisesRegex(TypeError, msg): + WOFF2FlavorData(reader, data=WOFF2FlavorData()) - def test_transformTables_default(self): + def test_transformedTables_default(self): flavorData = WOFF2FlavorData() self.assertEqual(flavorData.transformedTables, set(woff2TransformedTableTags)) - def test_transformTables_invalid(self): + def test_transformedTables_invalid(self): msg = r"'glyf' and 'loca' must be transformed \(or not\) together" with self.assertRaisesRegex(ValueError, msg): @@ -1258,6 +1261,24 @@ class MainTest(object): assert (tmpdir / "TestOTF-Regular.woff2").check(file=True) + def test_recompress_woff2_keeps_flavorData(self, tmpdir): + woff2_font = ttLib.TTFont(BytesIO(TT_WOFF2.getvalue())) + woff2_font.flavorData.privData = b"FOOBAR" + woff2_file = tmpdir / "TestTTF-Regular.woff2" + woff2_font.save(str(woff2_file)) + + assert woff2_font.flavorData.transformedTables == {"glyf", "loca"} + + woff2.main(["compress", "--hmtx-transform", str(woff2_file)]) + + output_file = tmpdir / "TestTTF-Regular#1.woff2" + assert output_file.check(file=True) + + new_woff2_font = ttLib.TTFont(str(output_file)) + + assert new_woff2_font.flavorData.transformedTables == {"glyf", "loca", "hmtx"} + assert new_woff2_font.flavorData.privData == b"FOOBAR" + def test_decompress_ttf(self, tmpdir): input_file = tmpdir / "TestTTF-Regular.woff2" input_file.write_binary(TT_WOFF2.getvalue()) From 1db016e841020de3b531b4f77261294cc3c8f285 Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Wed, 19 Jun 2019 12:23:14 +0100 Subject: [PATCH 5/8] Update changelog [skip ci] --- NEWS.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NEWS.rst b/NEWS.rst index 5880eadf3..42d18f931 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -1,3 +1,6 @@ +- [subset] Fixed regression when passing ``--flavor=woff2`` option with an input font + that was already compressed as WOFF 1.0 (#1650). + 3.43.0 (released 2019-06-18) ---------------------------- From 9c9cd233d6d27c461791dfa20f87992941268bd4 Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Wed, 19 Jun 2019 12:23:21 +0100 Subject: [PATCH 6/8] Release 3.43.1 --- Lib/fontTools/__init__.py | 2 +- NEWS.rst | 3 +++ setup.cfg | 2 +- setup.py | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Lib/fontTools/__init__.py b/Lib/fontTools/__init__.py index 5d6ca7f7f..d6919b289 100644 --- a/Lib/fontTools/__init__.py +++ b/Lib/fontTools/__init__.py @@ -5,6 +5,6 @@ from fontTools.misc.loggingTools import configLogger log = logging.getLogger(__name__) -version = __version__ = "3.43.1.dev0" +version = __version__ = "3.43.1" __all__ = ["version", "log", "configLogger"] diff --git a/NEWS.rst b/NEWS.rst index 42d18f931..b7b1b5064 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -1,3 +1,6 @@ +3.43.1 (released 2019-06-19) +---------------------------- + - [subset] Fixed regression when passing ``--flavor=woff2`` option with an input font that was already compressed as WOFF 1.0 (#1650). diff --git a/setup.cfg b/setup.cfg index 1f2fdfc0c..ee7a40c32 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 3.43.1.dev0 +current_version = 3.43.1 commit = True tag = False tag_name = {new_version} diff --git a/setup.py b/setup.py index b7f9f7097..ea4c92a98 100755 --- a/setup.py +++ b/setup.py @@ -352,7 +352,7 @@ def find_data_files(manpath="share/man"): setup( name="fonttools", - version="3.43.1.dev0", + version="3.43.1", description="Tools to manipulate font files", author="Just van Rossum", author_email="just@letterror.com", From 76489788d62bc0145d8d1f4eb3efdc4aca1d21c2 Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Wed, 19 Jun 2019 12:23:22 +0100 Subject: [PATCH 7/8] =?UTF-8?q?Bump=20version:=203.43.1=20=E2=86=92=203.43?= =?UTF-8?q?.2.dev0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Lib/fontTools/__init__.py | 2 +- setup.cfg | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/fontTools/__init__.py b/Lib/fontTools/__init__.py index d6919b289..60ea754f1 100644 --- a/Lib/fontTools/__init__.py +++ b/Lib/fontTools/__init__.py @@ -5,6 +5,6 @@ from fontTools.misc.loggingTools import configLogger log = logging.getLogger(__name__) -version = __version__ = "3.43.1" +version = __version__ = "3.43.2.dev0" __all__ = ["version", "log", "configLogger"] diff --git a/setup.cfg b/setup.cfg index ee7a40c32..eec867f2a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 3.43.1 +current_version = 3.43.2.dev0 commit = True tag = False tag_name = {new_version} diff --git a/setup.py b/setup.py index ea4c92a98..cb1c83666 100755 --- a/setup.py +++ b/setup.py @@ -352,7 +352,7 @@ def find_data_files(manpath="share/man"): setup( name="fonttools", - version="3.43.1", + version="3.43.2.dev0", description="Tools to manipulate font files", author="Just van Rossum", author_email="just@letterror.com", From 1122a2612b8daab832f1d56ca9bff4da74d2dc5c Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Thu, 20 Jun 2019 12:22:24 +0100 Subject: [PATCH 8/8] varLib: add set_default_weight_width_slant When building a variable font, varLib.build must make sure that the OS/2.usWeightClass is equal to the wght axis default location, that the OS/2.usWidthClass is set to the equivalent value (1-9) of the wdth axis default location, and finally that post.italicAngle is set to the same default value as slnt axis. Sometimes the base master doesn't have these values correctly set leading to discrepancies between OS/2 and post, on the one hand, and the fvar axes' default values. --- Lib/fontTools/varLib/__init__.py | 43 +++++++++++ Tests/varLib/data/test_results/BuildMain.ttx | 2 +- .../data/test_results/SparseMasters.ttx | 2 +- Tests/varLib/varLib_test.py | 77 +++++++++++++++++++ 4 files changed, 122 insertions(+), 2 deletions(-) diff --git a/Lib/fontTools/varLib/__init__.py b/Lib/fontTools/varLib/__init__.py index 891e92c54..66b193b2f 100644 --- a/Lib/fontTools/varLib/__init__.py +++ b/Lib/fontTools/varLib/__init__.py @@ -857,6 +857,45 @@ def load_designspace(designspace): ) +# https://docs.microsoft.com/en-us/typography/opentype/spec/os2#uswidthclass +WDTH_VALUE_TO_OS2_WIDTH_CLASS = { + 50: 1, + 62.5: 2, + 75: 3, + 87.5: 4, + 100: 5, + 112.5: 6, + 125: 7, + 150: 8, + 200: 9, +} + + +def set_default_weight_width_slant(font, location): + if "OS/2" in font: + if "wght" in location: + weight_class = otRound(max(1, min(location["wght"], 1000))) + if font["OS/2"].usWeightClass != weight_class: + log.info("Setting OS/2.usWidthClass = %s", weight_class) + font["OS/2"].usWeightClass = weight_class + + if "wdth" in location: + # map 'wdth' axis (50..200) to OS/2.usWidthClass (1..9), rounding to closest + widthValue = min(max(location["wdth"], 50), 200) + widthClass = otRound( + models.piecewiseLinearMap(widthValue, WDTH_VALUE_TO_OS2_WIDTH_CLASS) + ) + if font["OS/2"].usWidthClass != widthClass: + log.info("Setting OS/2.usWidthClass = %s", widthClass) + font["OS/2"].usWidthClass = widthClass + + if "slnt" in location and "post" in font: + italicAngle = max(-90, min(location["slnt"], 90)) + if font["post"].italicAngle != italicAngle: + log.info("Setting post.italicAngle = %s", italicAngle) + font["post"].italicAngle = italicAngle + + def build(designspace, master_finder=lambda s:s, exclude=[], optimize=True): """ Build variation font from a designspace file. @@ -930,6 +969,10 @@ def build(designspace, master_finder=lambda s:s, exclude=[], optimize=True): post.extraNames = [] post.mapping = {} + set_default_weight_width_slant( + vf, location={axis.axisTag: axis.defaultValue for axis in vf["fvar"].axes} + ) + for tag in exclude: if tag in vf: del vf[tag] diff --git a/Tests/varLib/data/test_results/BuildMain.ttx b/Tests/varLib/data/test_results/BuildMain.ttx index 7150a5793..7e5d9561f 100644 --- a/Tests/varLib/data/test_results/BuildMain.ttx +++ b/Tests/varLib/data/test_results/BuildMain.ttx @@ -55,7 +55,7 @@ will be recalculated by the compiler --> - + diff --git a/Tests/varLib/data/test_results/SparseMasters.ttx b/Tests/varLib/data/test_results/SparseMasters.ttx index 06e58c92f..c2aa335ce 100644 --- a/Tests/varLib/data/test_results/SparseMasters.ttx +++ b/Tests/varLib/data/test_results/SparseMasters.ttx @@ -55,7 +55,7 @@ will be recalculated by the compiler --> - + diff --git a/Tests/varLib/varLib_test.py b/Tests/varLib/varLib_test.py index e29befbf4..ec05d5657 100644 --- a/Tests/varLib/varLib_test.py +++ b/Tests/varLib/varLib_test.py @@ -4,6 +4,7 @@ from fontTools.ttLib import TTFont, newTable from fontTools.varLib import build from fontTools.varLib.mutator import instantiateVariableFont from fontTools.varLib import main as varLib_main, load_masters +from fontTools.varLib import set_default_weight_width_slant from fontTools.designspaceLib import ( DesignSpaceDocumentError, DesignSpaceDocument, SourceDescriptor, ) @@ -661,5 +662,81 @@ def _extract_flat_kerning(font, pairpos_table): return extracted_kerning +@pytest.fixture +def ttFont(): + f = TTFont() + f["OS/2"] = newTable("OS/2") + f["OS/2"].usWeightClass = 400 + f["OS/2"].usWidthClass = 100 + f["post"] = newTable("post") + f["post"].italicAngle = 0 + return f + + +class SetDefaultWeightWidthSlantTest(object): + @pytest.mark.parametrize( + "location, expected", + [ + ({"wght": 0}, 1), + ({"wght": 1}, 1), + ({"wght": 100}, 100), + ({"wght": 1000}, 1000), + ({"wght": 1001}, 1000), + ], + ) + def test_wght(self, ttFont, location, expected): + set_default_weight_width_slant(ttFont, location) + + assert ttFont["OS/2"].usWeightClass == expected + + @pytest.mark.parametrize( + "location, expected", + [ + ({"wdth": 0}, 1), + ({"wdth": 56}, 1), + ({"wdth": 57}, 2), + ({"wdth": 62.5}, 2), + ({"wdth": 75}, 3), + ({"wdth": 87.5}, 4), + ({"wdth": 100}, 5), + ({"wdth": 112.5}, 6), + ({"wdth": 125}, 7), + ({"wdth": 150}, 8), + ({"wdth": 200}, 9), + ({"wdth": 201}, 9), + ({"wdth": 1000}, 9), + ], + ) + def test_wdth(self, ttFont, location, expected): + set_default_weight_width_slant(ttFont, location) + + assert ttFont["OS/2"].usWidthClass == expected + + @pytest.mark.parametrize( + "location, expected", + [ + ({"slnt": -91}, -90), + ({"slnt": -90}, -90), + ({"slnt": 0}, 0), + ({"slnt": 11.5}, 11.5), + ({"slnt": 90}, 90), + ({"slnt": 91}, 90), + ], + ) + def test_slnt(self, ttFont, location, expected): + set_default_weight_width_slant(ttFont, location) + + assert ttFont["post"].italicAngle == expected + + def test_all(self, ttFont): + set_default_weight_width_slant( + ttFont, {"wght": 500, "wdth": 150, "slnt": -12.0} + ) + + assert ttFont["OS/2"].usWeightClass == 500 + assert ttFont["OS/2"].usWidthClass == 8 + assert ttFont["post"].italicAngle == -12.0 + + if __name__ == "__main__": sys.exit(unittest.main())