Skip to content

Commit

Permalink
Merge pull request #814 from googlefonts/component-hash-ttf
Browse files Browse the repository at this point in the history
Check glyph hashes more thoroughly
  • Loading branch information
anthrotype authored May 13, 2024
2 parents c85b363 + 7716a37 commit 779bbad
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 19 deletions.
28 changes: 16 additions & 12 deletions Lib/ufo2ft/instructionCompiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@

import array
import logging
from functools import partial
from typing import TYPE_CHECKING, Optional

from fontTools import ttLib
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,
Expand Down Expand Up @@ -42,27 +45,28 @@ 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 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

ttwidth = self.otf["hmtx"][glyphName][0]
ttwidth = self.otf["hmtx"][glyph.name][0]
hash_pen = HashPointPen(ttwidth, self.otf.getGlyphSet())
ttglyph.drawPoints(hash_pen, self.otf["glyf"])
round_pen = RoundingPointPen(
hash_pen, transformRoundFunc=partial(floatToFixedToFloat, precisionBits=14)
)
ttglyph.drawPoints(round_pen, self.otf["glyf"])

if glyph_hash != hash_pen.hash:
if stored_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 stored hash for glyph '{glyph.name}' does not match the "
"TrueType output glyph. Glyph will have no instructions in the font."
)
return False
return True
Expand Down Expand Up @@ -132,7 +136,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
Expand Down
1 change: 0 additions & 1 deletion tests/infoCompiler_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
18 changes: 12 additions & 6 deletions tests/instructionCompiler_test.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import logging
from functools import partial

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,
Expand Down Expand Up @@ -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


Expand Down Expand Up @@ -89,15 +95,15 @@ 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):
glyph = quaduforeversed["a"]

ic = InstructionCompiler(quaduforeversed, quadfont)
result = ic._check_glyph_hash(
glyph.name,
glyph,
quadfont["glyf"]["a"],
None,
)
Expand All @@ -113,7 +119,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,
)
Expand All @@ -129,7 +135,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,
)
Expand All @@ -146,7 +152,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,
)
Expand Down

0 comments on commit 779bbad

Please sign in to comment.