util/font: Add bitmapped font generation utility
This adds generate_font.py, a Python utility designed to convert standard TTF/OTF files into a compact bitmapped format suitable for firmware framebuffers. Key features: - Generates a left-aligned (normalized) bitmapped table to eliminate dead space in font glyphs. - Outputs a character width table to support proportional spacing. - Exports FONT_HEIGHT and FONT_WIDTH macros for C synchronization. - Limits output to printable ASCII (32-126) to minimize binary bloat. The tool uses the Pillow (PIL) library to rasterize glyphs and is intended to be used during the build process to generate C source files for splash screen text rendering. Usage: python util/font/generate_font.py <font.ttf> > font_table.c Change-Id: Iec8907f1a5f24d61822230f6a22295c8382d2229 Signed-off-by: Subrata Banik <subratabanik@google.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/91122 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Kapil Porwal <kapilporwal@google.com>
This commit is contained in:
parent
c078552e71
commit
8790998fa5
1 changed files with 124 additions and 0 deletions
124
util/font/generate_font.py
Normal file
124
util/font/generate_font.py
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
"""
|
||||
This utility generates a compressed bitmapped font table from a TTF/OTF file.
|
||||
The output is a C source file containing:
|
||||
1. Font dimensions and range macros.
|
||||
2. A normalized (left-aligned) bitmapped font table.
|
||||
3. A character width table for proportional spacing.
|
||||
|
||||
Usage:
|
||||
python generate_font.py <path_to_ttf> > font_table.c
|
||||
"""
|
||||
|
||||
from PIL import Image, ImageFont, ImageDraw
|
||||
import sys
|
||||
import os
|
||||
|
||||
# FONT DIMENSIONS
|
||||
FONT_SIZE = 19
|
||||
CANVAS_WIDTH = 16
|
||||
CANVAS_HEIGHT = 24
|
||||
Y_OFFSET = -2
|
||||
|
||||
# PRINTABLE ASCII RANGE
|
||||
START_CHAR = 32
|
||||
END_CHAR = 126
|
||||
NUM_CHARS = END_CHAR - START_CHAR + 1
|
||||
|
||||
def generate_c_table(font_path):
|
||||
if not os.path.exists(font_path):
|
||||
print(f"Error: Font file '{font_path}' not found.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
font = ImageFont.truetype(font_path, FONT_SIZE)
|
||||
except OSError:
|
||||
print(f"Error: Could not open font file '{font_path}'.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Header for the generated C file
|
||||
print("/* SPDX-License-Identifier: GPL-2.0-or-later */\n")
|
||||
print("/*")
|
||||
print(" * THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT.")
|
||||
print(f" * Source: {os.path.basename(font_path)}")
|
||||
print(f" * Command: python util/font/{os.path.basename(__file__)} {font_path}")
|
||||
print(" */\n")
|
||||
print("#include <stdint.h>\n")
|
||||
|
||||
# 1. Output the Macros for C code usage
|
||||
print(f"#define FONT_WIDTH {CANVAS_WIDTH}")
|
||||
print(f"#define FONT_HEIGHT {CANVAS_HEIGHT}")
|
||||
print(f"#define FONT_START_CHAR {START_CHAR}")
|
||||
print(f"#define FONT_END_CHAR {END_CHAR}")
|
||||
print(f"#define FONT_NUM_CHARS (FONT_END_CHAR - FONT_START_CHAR + 1)\n")
|
||||
|
||||
widths = []
|
||||
|
||||
# 2. Generate packed font table with block formatting
|
||||
print(f"const uint16_t font_table[FONT_NUM_CHARS][FONT_HEIGHT] = {{")
|
||||
|
||||
for char_code in range(START_CHAR, END_CHAR + 1):
|
||||
char = chr(char_code)
|
||||
image = Image.new("1", (CANVAS_WIDTH, CANVAS_HEIGHT), 0)
|
||||
draw = ImageDraw.Draw(image)
|
||||
draw.text((0, Y_OFFSET), char, font=font, fill=1)
|
||||
|
||||
pixels = list(image.getdata())
|
||||
rows = []
|
||||
global_mask = 0
|
||||
|
||||
for y in range(CANVAS_HEIGHT):
|
||||
row_val = 0
|
||||
base = y * CANVAS_WIDTH
|
||||
for x in range(CANVAS_WIDTH):
|
||||
if pixels[base + x]:
|
||||
row_val |= (1 << ((CANVAS_WIDTH - 1) - x))
|
||||
rows.append(row_val)
|
||||
global_mask |= row_val
|
||||
|
||||
# Dead space removal / Normalization
|
||||
left_shift = 0
|
||||
actual_width = 0
|
||||
if global_mask > 0:
|
||||
temp_mask = global_mask
|
||||
while not (temp_mask & 0x8000):
|
||||
temp_mask <<= 1
|
||||
left_shift += 1
|
||||
|
||||
rightmost_bit = 0
|
||||
for i in range(CANVAS_WIDTH):
|
||||
if (global_mask >> i) & 1:
|
||||
rightmost_bit = i
|
||||
break
|
||||
actual_width = ((CANVAS_WIDTH - 1) - left_shift) - rightmost_bit + 1
|
||||
else:
|
||||
actual_width = 6 # Default space width
|
||||
|
||||
widths.append(actual_width)
|
||||
|
||||
# Format the pre-shifted hex rows into blocks of 8
|
||||
hex_values = [f"0x{(row << left_shift) & 0xFFFF:04x}" for row in rows]
|
||||
char_repr = f"'{char}'" if char not in ["'", "\\"] else f"'\\{char}'"
|
||||
|
||||
print(f"\t[0x{char_code:02x} - FONT_START_CHAR] = {{")
|
||||
for i in range(0, len(hex_values), 8):
|
||||
line = ", ".join(hex_values[i:i+8])
|
||||
print(f"\t\t{line},")
|
||||
print(f"\t}}, /* {char_repr} */")
|
||||
|
||||
print("};\n")
|
||||
|
||||
# 3. Output Width Table
|
||||
print(f"const uint8_t font_widths[FONT_NUM_CHARS] = {{")
|
||||
for i, w in enumerate(widths):
|
||||
char_code = i + START_CHAR
|
||||
print(f"\t[0x{char_code:02x} - FONT_START_CHAR] = {w},")
|
||||
print("};")
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
print(f"Usage: python3 {sys.argv[0]} <path_to_ttf_file>")
|
||||
sys.exit(1)
|
||||
generate_c_table(sys.argv[1])
|
||||
Loading…
Add table
Add a link
Reference in a new issue