From 959369baef38506318875056706984d656c42436 Mon Sep 17 00:00:00 2001 From: Jens Kutilek Date: Tue, 23 Jan 2024 11:01:49 +0100 Subject: [PATCH 01/11] Compare UFO to TTF glyph using a rounding function for component scales --- Lib/ufo2ft/instructionCompiler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/ufo2ft/instructionCompiler.py b/Lib/ufo2ft/instructionCompiler.py index 86887e3f..35e0905b 100644 --- a/Lib/ufo2ft/instructionCompiler.py +++ b/Lib/ufo2ft/instructionCompiler.py @@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, Optional from fontTools import ttLib -from fontTools.pens.hashPointPen import HashPointPen +from fontTools.pens.hashPointPen import HashPointPen, ttScaleRound from fontTools.ttLib import newTable from fontTools.ttLib.tables._g_l_y_f import ( OVERLAP_COMPOUND, @@ -56,7 +56,7 @@ def _check_glyph_hash( # Check the glyph hash against the TTGlyph that is being built ttwidth = self.otf["hmtx"][glyphName][0] - hash_pen = HashPointPen(ttwidth, self.otf.getGlyphSet()) + hash_pen = HashPointPen(ttwidth, self.otf.getGlyphSet(), ttScaleRound) ttglyph.drawPoints(hash_pen, self.otf["glyf"]) if glyph_hash != hash_pen.hash: From ecfe76525535431ccfc001514f7d1e9945824479 Mon Sep 17 00:00:00 2001 From: Jens Kutilek Date: Tue, 23 Jan 2024 12:33:15 +0100 Subject: [PATCH 02/11] Combine HashPointPen with RoundingPointPen --- Lib/ufo2ft/instructionCompiler.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Lib/ufo2ft/instructionCompiler.py b/Lib/ufo2ft/instructionCompiler.py index 35e0905b..ecb17793 100644 --- a/Lib/ufo2ft/instructionCompiler.py +++ b/Lib/ufo2ft/instructionCompiler.py @@ -5,7 +5,9 @@ from typing import TYPE_CHECKING, Optional from fontTools import ttLib -from fontTools.pens.hashPointPen import HashPointPen, ttScaleRound +from fontTools.misc.fixedTools import floatToFixedToFloat +from fontTools.pens.hashPointPen import HashPointPen +from fontTools.pens.roundingPen import RoundingPointPen from fontTools.ttLib import newTable from fontTools.ttLib.tables._g_l_y_f import ( OVERLAP_COMPOUND, @@ -13,6 +15,7 @@ USE_MY_METRICS, flagOverlapSimple, ) +from functools import partial from ufo2ft.constants import ( OBJECT_LIBS_KEY, @@ -56,8 +59,11 @@ def _check_glyph_hash( # Check the glyph hash against the TTGlyph that is being built ttwidth = self.otf["hmtx"][glyphName][0] - hash_pen = HashPointPen(ttwidth, self.otf.getGlyphSet(), ttScaleRound) - ttglyph.drawPoints(hash_pen, self.otf["glyf"]) + hash_pen = HashPointPen(ttwidth, self.otf.getGlyphSet()) + round_pen = RoundingPointPen( + hash_pen, transformRoundFunc=partial(floatToFixedToFloat, precisionBits=14) + ) + ttglyph.drawPoints(round_pen, self.otf["glyf"]) if glyph_hash != hash_pen.hash: logger.error( From 4a38f40697f3fd71cfe594b981ed072b6ef104a8 Mon Sep 17 00:00:00 2001 From: Jens Kutilek Date: Tue, 23 Jan 2024 13:10:37 +0100 Subject: [PATCH 03/11] More thorough glyph hash checking --- Lib/ufo2ft/instructionCompiler.py | 41 +++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/Lib/ufo2ft/instructionCompiler.py b/Lib/ufo2ft/instructionCompiler.py index ecb17793..8413f73f 100644 --- a/Lib/ufo2ft/instructionCompiler.py +++ b/Lib/ufo2ft/instructionCompiler.py @@ -45,30 +45,51 @@ def __init__( self.autoUseMyMetrics = lambda ttGlyph, glyphName: None def _check_glyph_hash( - self, glyphName: str, ttglyph: TTGlyph, glyph_hash: Optional[str] + self, glyph: Glyph, ttglyph: TTGlyph, stored_hash: Optional[str] ) -> bool: - """Check if the supplied glyph hash from the ufo matches the current outlines.""" - if glyph_hash is None: + """Check if the supplied stored glyph hash from the ufo matches the current + outlines in the UFO and the TTGlyph.""" + if stored_hash is None: # The glyph hash is required logger.error( - f"Glyph hash missing, glyph '{glyphName}' will have " + f"Glyph hash missing, glyph '{glyph.name}' will have " "no instructions in font." ) return False - # Check the glyph hash against the TTGlyph that is being built + # Check the stored hash against the current UFO glyph - ttwidth = self.otf["hmtx"][glyphName][0] + hash_pen = HashPointPen(glyph.width, self.ufo) + glyph.drawPoints(hash_pen) + if stored_hash != hash_pen.hash: + logger.error( + f"The stored hash for glyph '{glyph.name}' does not match the current " + "UFO glyph. Glyph will have no instructions in the font." + ) + logger.error(f"Stored: {stored_hash}") + logger.error(f"Calced: {hash_pen.hash}") + return False + + # Check the calculated glyph hash against the TTGlyph that is being built + + hash_pen = HashPointPen(glyph.width, self.ufo) + round_pen = RoundingPointPen( + hash_pen, transformRoundFunc=partial(floatToFixedToFloat, precisionBits=14) + ) + glyph.drawPoints(round_pen) + ufo_hash = hash_pen.hash + + ttwidth = self.otf["hmtx"][glyph.name][0] hash_pen = HashPointPen(ttwidth, self.otf.getGlyphSet()) round_pen = RoundingPointPen( hash_pen, transformRoundFunc=partial(floatToFixedToFloat, precisionBits=14) ) ttglyph.drawPoints(round_pen, self.otf["glyf"]) - if glyph_hash != hash_pen.hash: + if ufo_hash != hash_pen.hash: logger.error( - f"The stored hash for glyph '{glyphName}' does not match the TrueType " - "output glyph. Glyph will have no instructions in the font." + f"The calculated hash for glyph '{glyph.name}' does not match the " + "TrueType output glyph. Glyph will have no instructions in the font." ) return False return True @@ -138,7 +159,7 @@ def _compile_tt_glyph_program( ) -> None: self._check_tt_data_format(ttdata, f"glyph '{glyph.name}'") glyph_hash = ttdata.get("id", None) - if not self._check_glyph_hash(glyph.name, ttglyph, glyph_hash): + if not self._check_glyph_hash(glyph, ttglyph, glyph_hash): return # Compile the glyph program From 9dbd5f0d42705d9a7c36ee7d23befe393669c27a Mon Sep 17 00:00:00 2001 From: Jens Kutilek Date: Tue, 23 Jan 2024 13:12:24 +0100 Subject: [PATCH 04/11] Update method call in tests --- tests/instructionCompiler_test.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/instructionCompiler_test.py b/tests/instructionCompiler_test.py index 87d2d66a..feda9f28 100644 --- a/tests/instructionCompiler_test.py +++ b/tests/instructionCompiler_test.py @@ -89,7 +89,7 @@ def test_check_glyph_hash_match(self, quaduforeversed, quadfont): ttglyph = quadfont["glyf"]["a"] ic = InstructionCompiler(quaduforeversed, quadfont) - result = ic._check_glyph_hash(glyph.name, ttglyph, ufo_hash) + result = ic._check_glyph_hash(glyph, ttglyph, ufo_hash) assert result def test_check_glyph_hash_missing(self, quaduforeversed, quadfont): @@ -97,7 +97,7 @@ def test_check_glyph_hash_missing(self, quaduforeversed, quadfont): ic = InstructionCompiler(quaduforeversed, quadfont) result = ic._check_glyph_hash( - glyph.name, + glyph, quadfont["glyf"]["a"], None, ) @@ -113,7 +113,7 @@ def test_check_glyph_hash_mismatch(self, testufo, quadfont): ic = InstructionCompiler(testufo, quadfont) result = ic._check_glyph_hash( - glyph.name, + glyph, ttglyph, ufo_hash, ) @@ -129,7 +129,7 @@ def test_check_glyph_hash_mismatch_composite(self, testufo, quadfont): ic = InstructionCompiler(testufo, quadfont) result = ic._check_glyph_hash( - glyph.name, + glyph, ttglyph, ufo_hash, ) @@ -146,7 +146,7 @@ def test_check_glyph_hash_mismatch_width(self, quaduforeversed, quadfont): ic = InstructionCompiler(quaduforeversed, quadfont) result = ic._check_glyph_hash( - glyph.name, + glyph, ttglyph, ufo_hash, ) From cbe077467897f4a6f8f646f8192db6c97842c36a Mon Sep 17 00:00:00 2001 From: Jens Kutilek Date: Tue, 23 Jan 2024 17:28:43 +0100 Subject: [PATCH 05/11] Sort imports --- Lib/ufo2ft/instructionCompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/ufo2ft/instructionCompiler.py b/Lib/ufo2ft/instructionCompiler.py index 8413f73f..d43a7a35 100644 --- a/Lib/ufo2ft/instructionCompiler.py +++ b/Lib/ufo2ft/instructionCompiler.py @@ -2,6 +2,7 @@ import array import logging +from functools import partial from typing import TYPE_CHECKING, Optional from fontTools import ttLib @@ -15,7 +16,6 @@ USE_MY_METRICS, flagOverlapSimple, ) -from functools import partial from ufo2ft.constants import ( OBJECT_LIBS_KEY, From 1926bb6a62d878649baebad00de0b46f2af27f89 Mon Sep 17 00:00:00 2001 From: Jens Kutilek Date: Wed, 8 May 2024 17:25:08 +0200 Subject: [PATCH 06/11] Only compare stored hash against calculated tt glyph hash --- Lib/ufo2ft/instructionCompiler.py | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/Lib/ufo2ft/instructionCompiler.py b/Lib/ufo2ft/instructionCompiler.py index d43a7a35..7309d508 100644 --- a/Lib/ufo2ft/instructionCompiler.py +++ b/Lib/ufo2ft/instructionCompiler.py @@ -57,27 +57,13 @@ def _check_glyph_hash( ) return False - # Check the stored hash against the current UFO glyph - - hash_pen = HashPointPen(glyph.width, self.ufo) - glyph.drawPoints(hash_pen) - if stored_hash != hash_pen.hash: - logger.error( - f"The stored hash for glyph '{glyph.name}' does not match the current " - "UFO glyph. Glyph will have no instructions in the font." - ) - logger.error(f"Stored: {stored_hash}") - logger.error(f"Calced: {hash_pen.hash}") - return False - - # Check the calculated glyph hash against the TTGlyph that is being built + # Check the stored glyph hash against the TTGlyph that is being built hash_pen = HashPointPen(glyph.width, self.ufo) round_pen = RoundingPointPen( hash_pen, transformRoundFunc=partial(floatToFixedToFloat, precisionBits=14) ) glyph.drawPoints(round_pen) - ufo_hash = hash_pen.hash ttwidth = self.otf["hmtx"][glyph.name][0] hash_pen = HashPointPen(ttwidth, self.otf.getGlyphSet()) @@ -86,9 +72,9 @@ def _check_glyph_hash( ) ttglyph.drawPoints(round_pen, self.otf["glyf"]) - if ufo_hash != hash_pen.hash: + if stored_hash != hash_pen.hash: logger.error( - f"The calculated hash for glyph '{glyph.name}' does not match the " + f"The stored hash for glyph '{glyph.name}' does not match the " "TrueType output glyph. Glyph will have no instructions in the font." ) return False From d2c1bc66ac72c310addd371cf1b5a89b53763560 Mon Sep 17 00:00:00 2001 From: Jens Kutilek Date: Wed, 8 May 2024 17:26:31 +0200 Subject: [PATCH 07/11] We don't need this --- Lib/ufo2ft/instructionCompiler.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Lib/ufo2ft/instructionCompiler.py b/Lib/ufo2ft/instructionCompiler.py index 7309d508..a77bc039 100644 --- a/Lib/ufo2ft/instructionCompiler.py +++ b/Lib/ufo2ft/instructionCompiler.py @@ -59,12 +59,6 @@ def _check_glyph_hash( # Check the stored glyph hash against the TTGlyph that is being built - hash_pen = HashPointPen(glyph.width, self.ufo) - round_pen = RoundingPointPen( - hash_pen, transformRoundFunc=partial(floatToFixedToFloat, precisionBits=14) - ) - glyph.drawPoints(round_pen) - ttwidth = self.otf["hmtx"][glyph.name][0] hash_pen = HashPointPen(ttwidth, self.otf.getGlyphSet()) round_pen = RoundingPointPen( From e47a89c241cfb233ec9627749b0861d2c5358ae0 Mon Sep 17 00:00:00 2001 From: Jens Kutilek Date: Wed, 8 May 2024 18:38:23 +0200 Subject: [PATCH 08/11] Fix tests --- tests/instructionCompiler_test.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/instructionCompiler_test.py b/tests/instructionCompiler_test.py index feda9f28..dd992a5a 100644 --- a/tests/instructionCompiler_test.py +++ b/tests/instructionCompiler_test.py @@ -2,7 +2,9 @@ import pytest from fontTools.cu2qu.ufo import font_to_quadratic +from fontTools.misc.fixedTools import floatToFixedToFloat from fontTools.pens.hashPointPen import HashPointPen +from fontTools.pens.roundingPen import RoundingPointPen from fontTools.ttLib.tables._g_l_y_f import ( OVERLAP_COMPOUND, ROUND_XY_TO_GRID, @@ -10,6 +12,7 @@ flagOverlapSimple, ) from fontTools.ttLib.ttFont import TTFont +from functools import partial from ufo2ft.instructionCompiler import InstructionCompiler @@ -40,7 +43,10 @@ def expect_maxp( def get_hash_ufo(glyph, ufo): hash_pen = HashPointPen(glyph.width, ufo) - glyph.drawPoints(hash_pen) + round_pen = RoundingPointPen( + hash_pen, transformRoundFunc=partial(floatToFixedToFloat, precisionBits=14) + ) + glyph.drawPoints(round_pen) return hash_pen.hash From 78c0a8ea593b1a939cd6d3ea97f7cdccc0a23602 Mon Sep 17 00:00:00 2001 From: Jens Kutilek Date: Wed, 8 May 2024 18:38:27 +0200 Subject: [PATCH 09/11] Lint --- tests/infoCompiler_test.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/infoCompiler_test.py b/tests/infoCompiler_test.py index 66ba6bbe..b35fa6ef 100644 --- a/tests/infoCompiler_test.py +++ b/tests/infoCompiler_test.py @@ -20,7 +20,6 @@ def testufo(FontClass): class InfoCompilerTest: - def test_head(self, testttf, testufo): info = {"versionMajor": 5, "versionMinor": 6} compiler = InfoCompiler(testttf, testufo, info) From 64bc4ed7125dc98ca5de1642029b9ae6ec402f4b Mon Sep 17 00:00:00 2001 From: Jens Kutilek Date: Wed, 8 May 2024 18:40:26 +0200 Subject: [PATCH 10/11] Lint again --- tests/instructionCompiler_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/instructionCompiler_test.py b/tests/instructionCompiler_test.py index dd992a5a..b06b251a 100644 --- a/tests/instructionCompiler_test.py +++ b/tests/instructionCompiler_test.py @@ -1,4 +1,5 @@ import logging +from functools import partial import pytest from fontTools.cu2qu.ufo import font_to_quadratic @@ -12,7 +13,6 @@ flagOverlapSimple, ) from fontTools.ttLib.ttFont import TTFont -from functools import partial from ufo2ft.instructionCompiler import InstructionCompiler From 7716a3734bb746f600e18a595d6eb8371a4aaa81 Mon Sep 17 00:00:00 2001 From: Jens Kutilek Date: Wed, 8 May 2024 18:45:24 +0200 Subject: [PATCH 11/11] Update comments --- Lib/ufo2ft/instructionCompiler.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Lib/ufo2ft/instructionCompiler.py b/Lib/ufo2ft/instructionCompiler.py index a77bc039..15675a94 100644 --- a/Lib/ufo2ft/instructionCompiler.py +++ b/Lib/ufo2ft/instructionCompiler.py @@ -47,8 +47,7 @@ def __init__( def _check_glyph_hash( self, glyph: Glyph, ttglyph: TTGlyph, stored_hash: Optional[str] ) -> bool: - """Check if the supplied stored glyph hash from the ufo matches the current - outlines in the UFO and the TTGlyph.""" + """Check if the supplied stored glyph hash from the ufo matches the TTGlyph.""" if stored_hash is None: # The glyph hash is required logger.error( @@ -57,8 +56,6 @@ def _check_glyph_hash( ) return False - # Check the stored glyph hash against the TTGlyph that is being built - ttwidth = self.otf["hmtx"][glyph.name][0] hash_pen = HashPointPen(ttwidth, self.otf.getGlyphSet()) round_pen = RoundingPointPen(