"""render_ascii.py — render ASCII 32-126 from Consolas TTF to grayscale bitmaps Output: CODE/firstInit/ascii_bitmaps.c (included by main.c) Glyph cell: 11x18. Consolas 18px has advance=10, ascender top ~3, descender ~18. ASCII_STEP=11 gives 1px spacing between glyphs (10px glyph + 1px gap). """ from PIL import Image, ImageFont, ImageDraw ttf = r"C:\Windows\Fonts\consola.ttf" GLYPH_W = 11 GLYPH_H = 18 FONT_SIZE = 18 font = ImageFont.truetype(ttf, FONT_SIZE) # Verify metrics on first glyph probe = font.getbbox("A") print(f"Consolas {FONT_SIZE}px — 'A' bbox={probe}, advance={font.getlength('A')}") lines = [] lines.append("/* Auto-generated by render_ascii.py - Consolas 18px grayscale */") lines.append("/* 95 glyphs: ASCII 32 (space) .. 126 (~), cell 11x18 */") lines.append("/* Do not edit - regenerate via: py render_ascii.py */") lines.append("") lines.append(f"static const int g_ascii_w = {GLYPH_W};") lines.append(f"static const int g_ascii_h = {GLYPH_H};") lines.append(f"static const unsigned char g_ascii[95][{GLYPH_W}*{GLYPH_H}] = {{") for cp in range(32, 127): ch = chr(cp) img = Image.new("L", (GLYPH_W, GLYPH_H), 0) draw = ImageDraw.Draw(img) draw.text((0, 0), ch, font=font, fill=255) pixels = list(img.tobytes()) if ch.isalnum(): comment = ch elif ch == ' ': comment = "space" else: comment = f"0x{cp:02x}" lines.append(f" /* '{comment}' */ {{") for y_row in range(GLYPH_H): row = ", ".join(str(pixels[y_row * GLYPH_W + x_col]) for x_col in range(GLYPH_W)) lines.append(f" {row},") lines.append(" },") lines.append("};") lines.append("") out_path = "CODE/firstInit/ascii_bitmaps.c" with open(out_path, "w", encoding="ascii") as f: f.write("\n".join(lines)) print(f"Generated {out_path} ({95*GLYPH_W*GLYPH_H} bytes of glyph data)")