feat: Add VLW font generation script and embedded font asset
This commit is contained in:
1 parent
7b23ffe119
commit
1c9bf0d113
2 files changed
+297
No files matched your search
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Generate a VLW font file compatible with LovyanGFX / M5GFX.
|
||||
|
||||
VLW format (all big-endian int32), verified against lgfx_fonts.cpp:
|
||||
Header (6 x int32):
|
||||
[0] glyph_count
|
||||
[1] vlw_encoder_version (write 8, matches Processing default)
|
||||
[2] font_size (points)
|
||||
[3] reserved / discard
|
||||
[4] ascent
|
||||
[5] descent
|
||||
|
||||
Per glyph (7 x int32), at offset 24:
|
||||
[0] codepoint
|
||||
[1] height
|
||||
[2] width
|
||||
[3] xAdvance
|
||||
[4] dY (y delta from baseline: ascent_px - bitmap_top)
|
||||
[5] gdX (x delta from cursor: bitmap_left / lsb)
|
||||
[6] reserved (0)
|
||||
|
||||
Bitmaps (at offset 24 + gCount*28):
|
||||
width * height bytes each, 1 byte per pixel (0=off, 255=on)
|
||||
"""
|
||||
|
||||
import struct
|
||||
import math
|
||||
import freetype
|
||||
|
||||
FONT_PATH = "/usr/share/fonts/Unifont/Unifont.ttf"
|
||||
SIZE_PT = 24
|
||||
DPI = 96
|
||||
GLYPHS = "ʘ⌐■★()>_<Ox "
|
||||
OUT = "unifont24.vlw"
|
||||
OUT_H = "../src/unifont24_vlw.h"
|
||||
|
||||
face = freetype.Face(FONT_PATH)
|
||||
face.set_char_size(SIZE_PT * 64, 0, DPI, 0)
|
||||
|
||||
metrics_face = face.size
|
||||
ascent_px = math.ceil(metrics_face.ascender / 64)
|
||||
descent_px = math.ceil(-metrics_face.descender / 64) # positive
|
||||
|
||||
glyphs_data = [] # (codepoint, height, width, xAdvance, dY, gdX, pixels)
|
||||
|
||||
for ch in GLYPHS:
|
||||
cp = ord(ch)
|
||||
# Grayscale render — gives 1 byte per pixel output
|
||||
face.load_char(cp, freetype.FT_LOAD_RENDER)
|
||||
slot = face.glyph
|
||||
bitmap = slot.bitmap
|
||||
|
||||
w = bitmap.width
|
||||
h = bitmap.rows
|
||||
gdX = slot.bitmap_left # x offset from cursor
|
||||
dY = slot.bitmap_top # y delta from baseline (above baseline = positive)
|
||||
xAdv = math.ceil(slot.advance.x / 64)
|
||||
|
||||
# Grayscale pitch may be wider than width — trim to width bytes per row
|
||||
# FreeType renders top-to-bottom in FT_PIXEL_MODE_GRAY — no reversal needed
|
||||
pitch = bitmap.pitch
|
||||
raw = bytes(bitmap.buffer)
|
||||
pixels = bytearray()
|
||||
for row in range(h):
|
||||
pixels += raw[row * pitch: row * pitch + w]
|
||||
|
||||
glyphs_data.append((cp, h, w, xAdv, dY, gdX, bytes(pixels)))
|
||||
print(f" U+{cp:04X} '{ch}' adv={xAdv} {w}x{h} gdX={gdX} dY={dY}")
|
||||
|
||||
# MUST be sorted ascending by codepoint — getUnicodeIndex uses lower_bound
|
||||
glyphs_data.sort(key=lambda g: g[0])
|
||||
|
||||
with open(OUT, "wb") as f:
|
||||
# Header — 6 int32s
|
||||
f.write(struct.pack(">iiiiii",
|
||||
len(glyphs_data), # [0] glyph count
|
||||
8, # [1] vlw encoder version
|
||||
SIZE_PT, # [2] font size in points
|
||||
0, # [3] reserved
|
||||
ascent_px, # [4] ascent
|
||||
descent_px, # [5] descent
|
||||
))
|
||||
# Glyph records
|
||||
for cp, h, w, xAdv, dY, gdX, _ in glyphs_data:
|
||||
f.write(struct.pack(">iiiiiii", cp, h, w, xAdv, dY, gdX, 0))
|
||||
# Bitmaps (1 byte per pixel)
|
||||
for _, _, _, _, _, _, pixels in glyphs_data:
|
||||
f.write(pixels)
|
||||
|
||||
print(f"\nWrote '{OUT}' ({len(glyphs_data)} glyphs, size={SIZE_PT}pt @ {DPI}dpi)")
|
||||
print(f"Ascent={ascent_px} Descent={descent_px}")
|
||||
|
||||
# C header for embedding in firmware flash
|
||||
vlw_bytes = open(OUT, "rb").read()
|
||||
with open(OUT_H, "w") as h_file:
|
||||
h_file.write("#pragma once\n")
|
||||
h_file.write("#include <stdint.h>\n\n")
|
||||
h_file.write(f"// Auto-generated by gen_vlw.py — {len(glyphs_data)} glyphs, {SIZE_PT}pt\n")
|
||||
h_file.write(f"// Usage: M5.Display.loadFont(unifont24_vlw);\n\n")
|
||||
h_file.write(f"static const uint8_t unifont24_vlw[] = {{\n")
|
||||
for i, b in enumerate(vlw_bytes):
|
||||
if i % 16 == 0:
|
||||
h_file.write(" ")
|
||||
h_file.write(f"0x{b:02X},")
|
||||
if i % 16 == 15:
|
||||
h_file.write("\n")
|
||||
else:
|
||||
h_file.write(" ")
|
||||
h_file.write("\n};\n")
|
||||
|
||||
print(f"Wrote '{OUT_H}' ({len(vlw_bytes)} bytes)")
|
||||
|
||||
|
||||
Reference in new issue
Block a user