Last active
April 17, 2025 23:25
-
-
Save nberlette/8770a3a422d31e726aa7d0ffde50e50d to your computer and use it in GitHub Desktop.
TypeScript Block Letter Renderer (ASCII or Unicode)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* This module provides a block letter rendering utility for text, | |
* capable of rendering arbitrary strings into multiple styles of | |
* block letters using either ASCII characters or Unicode blocks. | |
* | |
* It's perfect for creating stylized text in console applications! | |
* | |
* | |
* @license MIT (https://nick.mit-license.org) | |
* @author Nicholas Berlette <https://github.com/nberlette> | |
* @module block-letters | |
*/ | |
/** | |
* Represents the available font styles for use with {@linkcode renderText}. | |
* | |
* @category Types | |
*/ | |
export type Style = "solid" | "ascii" | "compact" | "tiny"; | |
/** | |
* A mapping from characters to their unicode block letter | |
* representation for the "solid" style (5 rows tall). | |
* | |
* @internal | |
*/ | |
const solidFont = { | |
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: ["███", " █", " █ ", "█ ", "███"], | |
" ": [" ", " ", " ", " ", " "], | |
}; | |
/** | |
* Translate the solidFont █ characters into ascii '*' for the "ascii" style. | |
*/ | |
const asciiFont: Record<string, string[]> = {}; | |
for (const [ch, rows] of Object.entries(solidFont)) { | |
asciiFont[ch] = rows.map((row) => row.replace(/█/g, "*")); | |
} | |
/** | |
* Collapse a base font into fewer rows by OR-ing groups of rows. | |
* @param base - the original font mapping | |
* @param groups - array of groups of row indices to merge | |
* @param fillChar - the character to treat as "filled" (defaults to '█') | |
*/ | |
function collapseFont( | |
base: Record<string, string[]>, | |
groups: number[][], | |
fillChar: string = "█", | |
): Record<string, string[]> { | |
const out: Record<string, string[]> = {}; | |
for (const [ch, rows] of Object.entries(base)) { | |
out[ch] = groups.map((group) => { | |
const width = rows[0].length; | |
let line = ""; | |
for (let col = 0; col < width; col++) { | |
const filled = group.some((r) => rows[r][col] === fillChar); | |
line += filled ? fillChar : " "; | |
} | |
return line; | |
}); | |
} | |
return out; | |
} | |
// Create two more compact versions by collapsing the solidFont: | |
// "compact" (3 rows): merge rows [0,1] → row0, [2] → row1, [3,4] → row2 | |
// "tiny" (2 rows): merge rows [0,1,2] → row0, [3,4] → row1 | |
const compactFont = collapseFont(solidFont, [ | |
[0, 1], | |
[2], | |
[3, 4], | |
]); | |
const tinyFont = collapseFont(solidFont, [ | |
[0, 1, 2], | |
[3, 4], | |
]); | |
/** | |
* Render text into styled unicode block letters. | |
* | |
* @example | |
* ```ts | |
* import { renderText } from "./block_letters.ts"; | |
* | |
* console.log(renderText("RATATUI", "solid")); | |
* console.log(renderText("RATATUI", "ascii")); | |
* console.log(renderText("RATATUI", "compact")); | |
* console.log(renderText("RATATUI", "tiny")); | |
* ``` | |
* | |
* @param input - The text to render. Only A–Z and space are supported. | |
* @param style - The block letter style to use. One of "solid" (5 rows), | |
* "ascii" (5 rows, '*' instead of '█'), | |
* "compact" (3 rows), | |
* or "tiny" (2 rows). Defaults to "solid". | |
* @returns The rendered string with newline-separated rows. | |
*/ | |
export function renderText( | |
input: string, | |
style: Style = "solid", | |
): string { | |
let font: Record<string, string[]>; | |
switch (style) { | |
case "ascii": | |
font = asciiFont; | |
break; | |
case "compact": | |
font = compactFont; | |
break; | |
case "tiny": | |
font = tinyFont; | |
break; | |
case "solid": | |
default: | |
font = solidFont; | |
break; | |
} | |
const rowsCount = font["A"].length; | |
const rows: string[] = Array(rowsCount).fill(""); | |
const text = input.toUpperCase(); | |
for (const ch of text) { | |
const charRows = font[ch] ?? font[" "]; | |
for (let i = 0; i < rowsCount; i++) { | |
rows[i] += charRows[i] + " "; | |
} | |
} | |
return rows.map((line) => line.trimEnd()).join("\n"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment