210 lines
12 KiB
Python
210 lines
12 KiB
Python
"""µFrame Big Text Fonts — multi-line ASCII art letter definitions.
|
||
|
||
Each font is a dict mapping characters to a list of strings (one per line).
|
||
All characters in a font have the same height.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Block font — solid █ with box-drawing. Height: 6 lines.
|
||
# ---------------------------------------------------------------------------
|
||
|
||
_BLOCK: dict[str, list[str]] = {
|
||
"A": [" █████╗ ", "██╔══██╗", "███████║", "██╔══██║", "██║ ██║", "╚═╝ ╚═╝"],
|
||
"B": ["██████╗ ", "██╔══██╗", "██████╔╝", "██╔══██╗", "██████╔╝", "╚═════╝ "],
|
||
"C": [" ██████╗", "██╔════╝", "██║ ", "██║ ", "╚██████╗", " ╚═════╝"],
|
||
"D": ["██████╗ ", "██╔══██╗", "██║ ██║", "██║ ██║", "██████╔╝", "╚═════╝ "],
|
||
"E": ["███████╗", "██╔════╝", "█████╗ ", "██╔══╝ ", "███████╗", "╚══════╝"],
|
||
"F": ["███████╗", "██╔════╝", "█████╗ ", "██╔══╝ ", "██║ ", "╚═╝ "],
|
||
"G": [" ██████╗ ", "██╔════╝ ", "██║ ███╗", "██║ ██║", "╚██████╔╝", " ╚═════╝ "],
|
||
"H": ["██╗ ██╗", "██║ ██║", "███████║", "██╔══██║", "██║ ██║", "╚═╝ ╚═╝"],
|
||
"I": ["██╗", "██║", "██║", "██║", "██║", "╚═╝"],
|
||
"J": [" ██╗", " ██║", " ██║", "██ ██║", "╚█████╔╝", " ╚════╝ "],
|
||
"K": ["██╗ ██╗", "██║ ██╔╝", "█████╔╝ ", "██╔═██╗ ", "██║ ██╗", "╚═╝ ╚═╝"],
|
||
"L": ["██╗ ", "██║ ", "██║ ", "██║ ", "███████╗", "╚══════╝"],
|
||
"M": ["███╗ ███╗", "████╗ ████║", "██╔████╔██║", "██║╚██╔╝██║", "██║ ╚═╝ ██║", "╚═╝ ╚═╝"],
|
||
"N": ["███╗ ██╗", "████╗ ██║", "██╔██╗ ██║", "██║╚██╗██║", "██║ ╚████║", "╚═╝ ╚═══╝"],
|
||
"O": [" ██████╗ ", "██╔═══██╗", "██║ ██║", "██║ ██║", "╚██████╔╝", " ╚═════╝ "],
|
||
"P": ["██████╗ ", "██╔══██╗", "██████╔╝", "██╔═══╝ ", "██║ ", "╚═╝ "],
|
||
"Q": [" ██████╗ ", "██╔═══██╗", "██║ ██║", "██║▄▄ ██║", "╚██████╔╝", " ╚══▀▀═╝ "],
|
||
"R": ["██████╗ ", "██╔══██╗", "██████╔╝", "██╔══██╗", "██║ ██║", "╚═╝ ╚═╝"],
|
||
"S": ["███████╗", "██╔════╝", "███████╗", "╚════██║", "███████║", "╚══════╝"],
|
||
"T": ["████████╗", "╚══██╔══╝", " ██║ ", " ██║ ", " ██║ ", " ╚═╝ "],
|
||
"U": ["██╗ ██╗", "██║ ██║", "██║ ██║", "██║ ██║", "╚██████╔╝", " ╚═════╝ "],
|
||
"V": ["██╗ ██╗", "██║ ██║", "██║ ██║", "╚██╗ ██╔╝", " ╚████╔╝ ", " ╚═══╝ "],
|
||
"W": ["██╗ ██╗", "██║ ██║", "██║ █╗ ██║", "██║███╗██║", "╚███╔███╔╝", " ╚══╝╚══╝ "],
|
||
"X": ["██╗ ██╗", "╚██╗██╔╝", " ╚███╔╝ ", " ██╔██╗ ", "██╔╝ ██╗", "╚═╝ ╚═╝"],
|
||
"Y": ["██╗ ██╗", "╚██╗ ██╔╝", " ╚████╔╝ ", " ╚██╔╝ ", " ██║ ", " ╚═╝ "],
|
||
"Z": ["███████╗", "╚══███╔╝", " ███╔╝ ", " ███╔╝ ", "███████╗", "╚══════╝"],
|
||
"0": [" ██████╗ ", "██╔═══██╗", "██║ ██║", "██║ ██║", "╚██████╔╝", " ╚═════╝ "],
|
||
"1": [" ██╗", "███║", "╚██║", " ██║", " ██║", " ╚═╝"],
|
||
"2": ["██████╗ ", "╚════██╗", " █████╔╝", "██╔═══╝ ", "███████╗", "╚══════╝"],
|
||
"3": ["██████╗ ", "╚════██╗", " █████╔╝", " ╚═══██╗", "██████╔╝", "╚═════╝ "],
|
||
"4": ["██╗ ██╗", "██║ ██║", "███████║", "╚════██║", " ██║", " ╚═╝"],
|
||
"5": ["███████╗", "██╔════╝", "███████╗", "╚════██║", "███████║", "╚══════╝"],
|
||
"6": [" ██████╗", "██╔════╝", "██████╗ ", "██╔══██╗", "╚█████╔╝", " ╚════╝ "],
|
||
"7": ["███████╗", "╚════██║", " ██╔╝", " ██╔╝ ", " ██║ ", " ╚═╝ "],
|
||
"8": [" █████╗ ", "██╔══██╗", "╚█████╔╝", "██╔══██╗", "╚█████╔╝", " ╚════╝ "],
|
||
"9": [" █████╗ ", "██╔══██╗", "╚██████║", " ╚═══██║", " █████╔╝", " ╚════╝ "],
|
||
"-": [" ", " ", "██████╗ ", "╚═════╝ ", " ", " "],
|
||
" ": [" ", " ", " ", " ", " ", " "],
|
||
".": [" ", " ", " ", " ", "██╗", "╚═╝"],
|
||
"!": ["██╗", "██║", "██║", "╚═╝", "██╗", "╚═╝"],
|
||
}
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Thin font — light single-stroke. Height: 3 lines.
|
||
# ---------------------------------------------------------------------------
|
||
|
||
_THIN: dict[str, list[str]] = {
|
||
"A": ["┌─┐", "├─┤", "┘ └"],
|
||
"B": ["┬─┐", "├─┤", "┴─┘"],
|
||
"C": ["┌─ ", "│ ", "└─ "],
|
||
"D": ["┬─┐", "│ │", "┴─┘"],
|
||
"E": ["┬──", "├─ ", "┴──"],
|
||
"F": ["┬──", "├─ ", "┘ "],
|
||
"G": ["┌─ ", "│ ┐", "└─┘"],
|
||
"H": ["┐ ┌", "├─┤", "┘ └"],
|
||
"I": ["┬", "│", "┴"],
|
||
"J": [" ┬", " │", "└─┘"],
|
||
"K": ["┐ ┌", "├┬┘", "┘└ "],
|
||
"L": ["│ ", "│ ", "└──"],
|
||
"M": ["┌┬┐", "│││", "┘ └"],
|
||
"N": ["┌┐ ", "│└┐", "┘ └"],
|
||
"O": ["┌─┐", "│ │", "└─┘"],
|
||
"P": ["┌─┐", "├─┘", "┘ "],
|
||
"Q": ["┌─┐", "│ │", "└─┤"],
|
||
"R": ["┌─┐", "├─┤", "┘ └"],
|
||
"S": ["┌─ ", "└─┐", " ─┘"],
|
||
"T": ["┬─┬", " │ ", " ┴ "],
|
||
"U": ["┐ ┌", "│ │", "└─┘"],
|
||
"V": ["┐ ┌", "│ │", "└┬┘"],
|
||
"W": ["┐ ┌", "│││", "└┴┘"],
|
||
"X": ["╲ ╱", " ╳ ", "╱ ╲"],
|
||
"Y": ["┐ ┌", "└┬┘", " ┴ "],
|
||
"Z": ["──┐", " ╱ ", "└──"],
|
||
"0": ["┌─┐", "│ │", "└─┘"],
|
||
"1": [" ┐", " │", " ┴"],
|
||
"2": ["─┐", "┌┘", "└─"],
|
||
"3": ["─┐", " ┤", "─┘"],
|
||
"4": ["┐ ┐", "└─┤", " ┘"],
|
||
"5": ["┌─", "└┐", "─┘"],
|
||
"6": ["┌─", "├┐", "└┘"],
|
||
"7": ["──┐", " │", " ┘"],
|
||
"8": ["┌┐", "├┤", "└┘"],
|
||
"9": ["┌┐", "└┤", "─┘"],
|
||
"-": [" ", "── ", " "],
|
||
" ": [" ", " ", " "],
|
||
".": [" ", " ", "·"],
|
||
"!": ["│", " ", "·"],
|
||
}
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Pixel font — retro bitmap. Height: 3 lines.
|
||
# ---------------------------------------------------------------------------
|
||
|
||
_PIXEL: dict[str, list[str]] = {
|
||
"A": ["▀▀▀▄", "█▀▀█", "▀ ▀"],
|
||
"B": ["▀▀▀▄", "█▀▀▄", "▀▀▀ "],
|
||
"C": ["▄▀▀▀", "█ ", "▀▀▀ "],
|
||
"D": ["▀▀▀▄", "█ █", "▀▀▀ "],
|
||
"E": ["▀▀▀ ", "█▀ ", "▀▀▀ "],
|
||
"F": ["▀▀▀ ", "█▀ ", "▀ "],
|
||
"G": ["▄▀▀ ", "█ ▀█", "▀▀▀ "],
|
||
"H": ["▀ ▀ ", "█▀▀█", "▀ ▀"],
|
||
"I": ["▀", "█", "▀"],
|
||
"J": [" ▀ ", " █ ", "▀▀ "],
|
||
"K": ["▀ ▄ ", "█▀▄ ", "▀ ▀"],
|
||
"L": ["▀ ", "█ ", "▀▀▀ "],
|
||
"M": ["▀▄▄▀", "█▀▀█", "▀ ▀"],
|
||
"N": ["▀▄ ▀", "█ ▀█", "▀ ▀"],
|
||
"O": ["▄▀▀▄", "█ █", "▀▀▀ "],
|
||
"P": ["▀▀▀▄", "█▀▀ ", "▀ "],
|
||
"Q": ["▄▀▀▄", "█ █", "▀▀▀▄"],
|
||
"R": ["▀▀▀▄", "█▀▀▄", "▀ ▀"],
|
||
"S": ["▄▀▀ ", "▀▀▀▄", " ▀▀ "],
|
||
"T": ["▀▀▀▀", " █ ", " ▀ "],
|
||
"U": ["▀ ▀", "█ █", "▀▀▀ "],
|
||
"V": ["▀ ▀", "█ █", " ▀▀ "],
|
||
"W": ["▀ ▀", "█▄▄█", "▀▀▀▀"],
|
||
"X": ["▀ ▀", " ▀▀ ", "▀ ▀"],
|
||
"Y": ["▀ ▀", " ▀▀ ", " ▀ "],
|
||
"Z": ["▀▀▀▀", " ▄▀ ", "▀▀▀▀"],
|
||
"0": ["▄▀▀▄", "█ █", "▀▀▀ "],
|
||
"1": [" ▄", " █", " ▀"],
|
||
"2": ["▀▀▄", " ▄▀", "▀▀▀"],
|
||
"3": ["▀▀▄", " ▀▄", "▀▀ "],
|
||
"4": ["▀ ▀", "▀▀█", " ▀"],
|
||
"5": ["▀▀▀", "▀▀▄", "▀▀ "],
|
||
"6": ["▄▀▀", "█▀▄", "▀▀ "],
|
||
"7": ["▀▀▀", " █", " ▀"],
|
||
"8": ["▄▀▄", "█▀█", "▀▀ "],
|
||
"9": ["▄▀▄", "▀▀█", "▀▀ "],
|
||
"-": [" ", " ▀▀ ", " "],
|
||
" ": [" ", " ", " "],
|
||
".": [" ", " ", "▄"],
|
||
"!": ["█", " ", "▄"],
|
||
}
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Font registry
|
||
# ---------------------------------------------------------------------------
|
||
|
||
FONTS: dict[str, dict[str, list[str]]] = {
|
||
"block": _BLOCK,
|
||
"thin": _THIN,
|
||
"pixel": _PIXEL,
|
||
}
|
||
|
||
FONT_HEIGHTS: dict[str, int] = {
|
||
"block": 6,
|
||
"thin": 3,
|
||
"pixel": 3,
|
||
}
|
||
|
||
|
||
def render_big_text(text: str, font_name: str = "block", kerning: int = 1) -> list[str]:
|
||
"""Render text as multi-line ASCII art using the specified font.
|
||
|
||
Args:
|
||
text: the string to render (uppercase recommended for block/pixel)
|
||
font_name: "block", "thin", or "pixel"
|
||
kerning: space between characters (0=tight, 1=normal, 2=wide)
|
||
|
||
Returns:
|
||
List of strings, one per line of the rendered text.
|
||
"""
|
||
font = FONTS.get(font_name, _BLOCK)
|
||
height = FONT_HEIGHTS.get(font_name, 6)
|
||
text_upper = text.upper()
|
||
|
||
# Build each line by concatenating character columns
|
||
lines: list[str] = ["" for _ in range(height)]
|
||
spacer = " " * kerning
|
||
|
||
for i, ch in enumerate(text_upper):
|
||
glyph = font.get(ch, font.get("?", [" " * 3] * height))
|
||
for row in range(height):
|
||
if row < len(glyph):
|
||
lines[row] += glyph[row]
|
||
else:
|
||
lines[row] += " " * (len(glyph[0]) if glyph else 3)
|
||
if i < len(text_upper) - 1:
|
||
lines[row] += spacer
|
||
|
||
return lines
|
||
|
||
|
||
def get_text_width(text: str, font_name: str = "block", kerning: int = 1) -> int:
|
||
"""Calculate the rendered width of big text without rendering it."""
|
||
font = FONTS.get(font_name, _BLOCK)
|
||
text_upper = text.upper()
|
||
width = 0
|
||
for i, ch in enumerate(text_upper):
|
||
glyph = font.get(ch, font.get("?", [" " * 3]))
|
||
width += len(glyph[0]) if glyph else 3
|
||
if i < len(text_upper) - 1:
|
||
width += kerning
|
||
return width
|