#!/usr/bin/env python3 """Display SSD1306 vertical-byte glyph/icon arrays as console text. Handles two array shapes found in C headers: Single-page glyphs uint8_t name[][W] Each row is one glyph: W columns × 8 rows. Displayed side-by-side in a grid. Two-page icons uint8_t name[...][2][W] or name[2][W] Each pair of rows is one icon: W columns × 16 rows. Outer 1-pixel border trimmed → 14×14 display. Usage: python3 icons.py [FILE] show all glyphs and icons (default: main/font.h) """ import re import sys # ── Glyph display (single page, arbitrary W×8) ─────────────────────────────── def decode_glyph(data): """data: list of W ints → 8×W bitmap.""" w = len(data) return [[(data[col] >> row) & 1 for col in range(w)] for row in range(8)] def show_glyphs(entries, per_row=10): """entries: list of (label, data) — print glyphs side by side.""" if not entries: return glyph_w = len(entries[0][1]) col_w = max(glyph_w, max(len(label) for label, _ in entries)) sep = " " for i in range(0, len(entries), per_row): chunk = entries[i : i + per_row] grids = [decode_glyph(data) for _, data in chunk] labels = [label for label, _ in chunk] print(sep.join(f"{lbl:^{col_w}}" for lbl in labels)) for row in range(8): parts = ["".join("x" if grids[g][row][c] else " " for c in range(glyph_w)) for g in range(len(chunk))] print(sep.join(f"{p:^{col_w}}" for p in parts)) print() # ── Icon display (two pages, W×16 → trimmed 14×14) ─────────────────────────── def decode_icon(data): """data: [[W ints page0], [W ints page1]] → 14×14 list-of-lists.""" w = len(data[0]) rows = [[(data[page][col] >> bit) & 1 for col in range(w)] for page in range(2) for bit in range(8)] return [r[1:-1] for r in rows[1:-1]] def show_icon(grid, name=""): if name: print(f"── {name} ──") for row in grid: print("".join("x" if p else " " for p in row)) print() # ── C file parser ───────────────────────────────────────────────────────────── def _hex_row(raw): return [int(x, 16) for x in re.findall(r"0x[0-9A-Fa-f]+", raw)] def parse_file(path): """ Parse all uint8_t byte arrays from a C header. Returns: glyphs: dict of array_name → list of (label, [W bytes]) icons: dict of name → [[W page0 bytes], [W page1 bytes]] """ text = open(path).read() glyphs = {} icons = {} # ── Two-page icon arrays ────────────────────────────────────────────────── # Matches: uint8_t name[...][2][W] = { ... }; # Also: uint8_t name[2][W] = { ... }; for m in re.finditer( r"uint8_t\s+(\w+)\s*(?:\[[^\]]*\])*\[2\]\[([^\]]+)\]\s*=\s*\{(.*?)\}\s*;", text, re.DOTALL, ): array_name = m.group(1) body = m.group(3) # Pull each { {top}, {bot} } pair, with an optional // comment before it pairs = re.findall( r"(?://\s*(.+?)\n)?\s*\{\s*\{([^}]+)\}\s*,\s*\{([^}]+)\}\s*\}", body, ) if pairs: for i, (comment, raw0, raw1) in enumerate(pairs): label = comment.strip() if comment.strip() else f"{array_name}[{i}]" icons[label] = [_hex_row(raw0), _hex_row(raw1)] else: # Standalone name[2][W] — whole array is one icon rows = re.findall(r"\{([^}]+)\}", body) if len(rows) == 2: icons[array_name] = [_hex_row(rows[0]), _hex_row(rows[1])] # ── Single-page glyph arrays ────────────────────────────────────────────── # Matches: uint8_t name[][W] = { {bytes}, // label \n ... }; for m in re.finditer( r"uint8_t\s+(\w+)\s*\[\]\s*\[([^\]]+)\]\s*=\s*\{(.*?)\}\s*;", text, re.DOTALL, ): array_name = m.group(1) body = m.group(3) entries = [] for em in re.finditer(r"\{([^}]+)\}\s*(?:,\s*)?(?://\s*(.+))?", body): data = _hex_row(em.group(1)) comment = (em.group(2) or "").strip().strip("'\"") label = comment if comment else str(len(entries)) entries.append((label, data)) if entries: glyphs[array_name] = entries return glyphs, icons # ── Entry point ─────────────────────────────────────────────────────────────── if __name__ == "__main__": path = next((a for a in sys.argv[1:] if not a.startswith("--")), "main/font.h") glyphs, icons = parse_file(path) if not glyphs and not icons: sys.exit(f"no glyph or icon arrays found in {path}") for array_name, entries in glyphs.items(): print(f"{'═' * 60}") print(f" {array_name} ({len(entries)} glyphs)") print(f"{'═' * 60}\n") show_glyphs(entries) for name, data in icons.items(): show_icon(decode_icon(data), name)