#!/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 = "ʘ⌐■★()>_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 \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)")