feat(core): extract kernings from ttf files, update mako template.
What changed, and why it matters
This commit is a routine feature addition to the font-generation tooling for the Trezor hardware wallet firmware. It teaches the build-time font generator to read kerning (letter-spacing adjustment) data from TrueType font files and include that data in the generated Rust font tables. There is no indication this change fixes a security bug or introduces a security-relevant behavior.
No security action required. Treat as a normal build-tooling feature commit.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change modifies two build-time code-generation files: gen_font.py and gen_font.mako. gen_font.py now iterates over glyph pairs, calls FreeType’s get_kerning(), and stores non-zero kerning values as (u8, u8, i8) triples. The Mako template emits a new Font_*_kernings array and adds a kernings field to the generated FontInfo structs. This is purely a font-rendering feature; no runtime code, parsing logic, or cryptographic code is touched.
Changed components
core/tools/codegen/gen_font.pycore/tools/codegen/gen_font.makoInspect captured patch +31 / −0
diff --git a/core/tools/codegen/gen_font.mako b/core/tools/codegen/gen_font.mako
index e0a15942..3aa911ab 100644
--- a/core/tools/codegen/gen_font.mako
+++ b/core/tools/codegen/gen_font.mako
@@ -35,6 +35,16 @@ const Font_${name}_upper: [&[u8]; ${len(glyph_array_upper)}] = [
% endif
% if gen_normal:
+/// Array of kerning tripples
+const Font_${name}_kernings: [(u8, u8, i8); ${len(kernings)}] = [
+% for ref in kernings:
+ ${ref}, // ${chr(ref[0])} + ${chr(ref[1])}
+%endfor
+];
+% endif
+% if gen_normal:
+
+
/// FontInfo struct for normal ASCII usage
pub const Font_${name}_info: FontInfo = FontInfo {
translation_blob_idx: ${font_info["translation_blob_idx"]},
@@ -43,6 +53,7 @@ pub const Font_${name}_info: FontInfo = FontInfo {
baseline: ${font_info["baseline"]},
glyph_data: &${font_info["glyph_array"]},
glyph_nonprintable: &${font_info["nonprintable"]},
+ kernings: &${font_info["kernings"]},
};
% endif
% if gen_upper:
@@ -55,5 +66,6 @@ pub const Font_${name}_upper_info: FontInfo = FontInfo {
baseline: ${font_info_upper["baseline"]},
glyph_data: &${font_info_upper["glyph_array"]},
glyph_nonprintable: &${font_info_upper["nonprintable"]},
+ kernings: &${font_info_upper["kernings"]},
};
% endif
diff --git a/core/tools/codegen/gen_font.py b/core/tools/codegen/gen_font.py
index 045633f7..4953ca00 100755
--- a/core/tools/codegen/gen_font.py
+++ b/core/tools/codegen/gen_font.py
@@ -431,6 +431,22 @@ class FaceProcessor:
self.font_ymin = min(self.font_ymin, yMin)
self.font_ymax = max(self.font_ymax, yMax)
+ kernings = []
+ for left in range(MIN_GLYPH, MAX_GLYPH + 1):
+ for right in range(MIN_GLYPH, MAX_GLYPH + 1):
+ kerning = self.face.get_kerning(
+ left, right, freetype.FT_KERNING_DEFAULT
+ )
+ if kerning.x != 0:
+ kernings.append((left, right, kerning.x // 64))
+ print(
+ f"left glyph: {chr(left)} right glyph:{chr(right)} kerning {kerning.x // 64}"
+ )
+
+ print(
+ f"Font: {self._name_style_size} {self.style} {self.size} : Num of kernirngs {len(kernings)}"
+ )
+
# 5) Build FontInfo definitions.
font_info = None
font_info_upper = None
@@ -447,6 +463,7 @@ class FaceProcessor:
"baseline": -self.font_ymin,
"glyph_array": f"Font_{self._name_style_size}",
"nonprintable": f"Font_{self._name_style_size}_glyph_nonprintable",
+ "kernings": f"Font_{self._name_style_size}_kernings",
}
if self.gen_upper:
if self.font_idx_upper is None:
@@ -461,6 +478,7 @@ class FaceProcessor:
"baseline": -self.font_ymin,
"glyph_array": f"Font_{self._name_style_size}_upper",
"nonprintable": f"Font_{self._name_style_size}_glyph_nonprintable",
+ "kernings": f"Font_{self._name_style_size}_kernings",
}
data = {
@@ -470,6 +488,7 @@ class FaceProcessor:
"nonprintable": nonprintable,
"glyph_array": glyph_array,
"glyph_array_upper": glyph_array_upper,
+ "kernings": kernings,
"gen_normal": self.gen_normal,
"gen_upper": self.gen_upper,
"font_info": font_info,
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.