From 311bda202156ea14af840db753c52a59fb5f54c0 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Wed, 24 May 2023 17:34:39 -0600 Subject: [PATCH 1/4] [varStore] Add optional quantization to optimize() --- Lib/fontTools/varLib/varStore.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Lib/fontTools/varLib/varStore.py b/Lib/fontTools/varLib/varStore.py index dcc36bb1e..8c1b94893 100644 --- a/Lib/fontTools/varLib/varStore.py +++ b/Lib/fontTools/varLib/varStore.py @@ -470,7 +470,7 @@ class _EncodingDict(dict): return chars -def VarStore_optimize(self, use_NO_VARIATION_INDEX=True): +def VarStore_optimize(self, use_NO_VARIATION_INDEX=True, quantization=1): """Optimize storage. Returns mapping from old VarIdxes to new ones.""" # Overview: @@ -551,8 +551,14 @@ def VarStore_optimize(self, use_NO_VARIATION_INDEX=True): for minor, item in enumerate(data.Item): row = list(zeroes) - for regionIdx, v in zip(regionIndices, item): - row[regionIdx] += v + + if quantization == 1: + for regionIdx, v in zip(regionIndices, item): + row[regionIdx] += v + else: + for regionIdx, v in zip(regionIndices, item): + row[regionIdx] += v // quantization * quantization + row = tuple(row) if use_NO_VARIATION_INDEX and not any(row): From d9649a7835f4bc7dd79a5c4b974515638eb959ee Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Wed, 24 May 2023 18:00:19 -0600 Subject: [PATCH 2/4] [varStore] Add test for quantization --- Lib/fontTools/varLib/varStore.py | 4 ++- Tests/varLib/varStore_test.py | 54 ++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/Lib/fontTools/varLib/varStore.py b/Lib/fontTools/varLib/varStore.py index 8c1b94893..2e3d9a587 100644 --- a/Lib/fontTools/varLib/varStore.py +++ b/Lib/fontTools/varLib/varStore.py @@ -557,7 +557,9 @@ def VarStore_optimize(self, use_NO_VARIATION_INDEX=True, quantization=1): row[regionIdx] += v else: for regionIdx, v in zip(regionIndices, item): - row[regionIdx] += v // quantization * quantization + row[regionIdx] += ( + round(v / quantization) * quantization + ) # TODO: round towards 0 row = tuple(row) diff --git a/Tests/varLib/varStore_test.py b/Tests/varLib/varStore_test.py index 37447c051..70d1340f2 100644 --- a/Tests/varLib/varStore_test.py +++ b/Tests/varLib/varStore_test.py @@ -202,3 +202,57 @@ def test_optimize(numRegions, varData, expectedNumVarData, expectedBytes): data = writer.getAllData() assert len(data) == expectedBytes, xml + + +@pytest.mark.parametrize( + "quantization, expectedBytes", + [ + (1, 200), + (2, 180), + (3, 170), + (4, 175), + (8, 170), + (32, 152), + (64, 146), + ], +) +def test_quantize(quantization, expectedBytes): + varData = [ + [0, 11, 12, 0, 20], + [0, 13, 12, 0, 20], + [0, 14, 12, 0, 20], + [0, 15, 12, 0, 20], + [0, 16, 12, 0, 20], + [10, 300, 0, 0, 20], + [10, 301, 0, 0, 20], + [10, 302, 0, 0, 20], + [10, 303, 0, 0, 20], + [10, 304, 0, 0, 20], + ] + + numRegions = 5 + locations = [{i: i / 16384.0} for i in range(numRegions)] + axisTags = sorted({k for loc in locations for k in loc}) + + model = VariationModel(locations) + + builder = OnlineVarStoreBuilder(axisTags) + builder.setModel(model) + + for data in varData: + builder.storeMasters(data) + + varStore = builder.finish() + varStore.optimize(quantization=quantization) + + dummyFont = TTFont() + + writer = XMLWriter(StringIO()) + varStore.toXML(writer, dummyFont) + xml = writer.file.getvalue() + + writer = OTTableWriter() + varStore.compile(writer, dummyFont) + data = writer.getAllData() + + assert len(data) == expectedBytes, xml From ae7394e68a2afcbf27d30fc7d95c445bf5a034a2 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Wed, 24 May 2023 18:05:07 -0600 Subject: [PATCH 3/4] [varLib.main] Accept --quantization --- Lib/fontTools/varLib/varStore.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Lib/fontTools/varLib/varStore.py b/Lib/fontTools/varLib/varStore.py index 2e3d9a587..2f5923f60 100644 --- a/Lib/fontTools/varLib/varStore.py +++ b/Lib/fontTools/varLib/varStore.py @@ -692,6 +692,7 @@ def main(args=None): from fontTools.ttLib.tables.otBase import OTTableWriter parser = ArgumentParser(prog="varLib.varStore", description=main.__doc__) + parser.add_argument("--quantization", type=int, default=1) parser.add_argument("fontfile") parser.add_argument("outfile", nargs="?") options = parser.parse_args(args) @@ -699,6 +700,7 @@ def main(args=None): # TODO: allow user to configure logging via command-line options configLogger(level="INFO") + quantization = options.quantization fontfile = options.fontfile outfile = options.outfile @@ -711,7 +713,7 @@ def main(args=None): size = len(writer.getAllData()) print("Before: %7d bytes" % size) - varidx_map = store.optimize() + varidx_map = store.optimize(quantization=quantization) writer = OTTableWriter() store.compile(writer, font) From a08f5ebbc9b748435d8d9342726911f2affae3e7 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Thu, 25 May 2023 06:24:00 -0600 Subject: [PATCH 4/4] [varStore] Update comment --- Lib/fontTools/varLib/varStore.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/fontTools/varLib/varStore.py b/Lib/fontTools/varLib/varStore.py index 2f5923f60..f05f41f5d 100644 --- a/Lib/fontTools/varLib/varStore.py +++ b/Lib/fontTools/varLib/varStore.py @@ -559,7 +559,7 @@ def VarStore_optimize(self, use_NO_VARIATION_INDEX=True, quantization=1): for regionIdx, v in zip(regionIndices, item): row[regionIdx] += ( round(v / quantization) * quantization - ) # TODO: round towards 0 + ) # TODO https://github.com/fonttools/fonttools/pull/3126#discussion_r1205439785 row = tuple(row)