https://github.com/pfahlr/figlet-font-curator
Scans a directory of figlet fonts comparing them to another directory (your collection) and copies any that do not match any of the fonts already in your collection.
https://github.com/pfahlr/figlet-font-curator
1990s 2000s ascii ascii-art cli collection command-line curated figlet figlet-fonts fonts retro terminal
Last synced: about 1 month ago
JSON representation
Scans a directory of figlet fonts comparing them to another directory (your collection) and copies any that do not match any of the fonts already in your collection.
- Host: GitHub
- URL: https://github.com/pfahlr/figlet-font-curator
- Owner: pfahlr
- Created: 2025-10-03T05:37:04.000Z (9 months ago)
- Default Branch: main
- Last Pushed: 2025-10-03T07:10:40.000Z (8 months ago)
- Last Synced: 2025-10-03T07:20:54.575Z (8 months ago)
- Topics: 1990s, 2000s, ascii, ascii-art, cli, collection, command-line, curated, figlet, figlet-fonts, fonts, retro, terminal
- Language: Python
- Homepage:
- Size: 8.98 MB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
```
██████████████████████████████████████████████████████████
█▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█
█▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓█
█▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓█
█▓░ ░▓█
█▓░ ╭╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮ ░▓█
█▓░ ┊ ### ┊ ░▓█
█▓░ ┊ ####### ### ###### ### ######## ######## ┊ ░▓█
█▓░ ┊ ### ### ### ### ┊ ░▓█
█▓░ ┊ ####### ### ### ## ### ####### ### ┊ ░▓█
█▓░ ┊ ## ### ### ## ### ### ### ┊ ░▓█
█▓░ ┊ ## ### ###### ####### ####### ### ┊ ░▓█
█▓░ ┊ ========================================== ┊ ░▓█
█▓░ ┊ __ ┊ ░▓█
█▓░ ┊ _______ ___________ _/ /_____ _____ ┊ ░▓█
█▓░ ┊ / ___/ / / / ___/ __ `/ __/ __ \/ ___/ ┊ ░▓█
█▓░ ┊ / /__/ /_/ / / / /_/ / /_/ /_/ / / ┊ ░▓█
█▓░ ┊ \___/\__,_/_/ \__,_/\__/\____/_/ ┊ ░▓█
█▓░ ╰╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╯ ░▓█
█▓░ ░▓█
█▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▓█
█▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓█
█▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█
██████████████████████████████████████████████████████████
```
# FIGlet Font Curator & Browser
A two-part toolkit for building and exploring a clean, well-organized **FIGlet** font collection:
1. **Curator (CLI)** — import `.flf`/`.tlf`/`.flc`, de-duplicate, organize into a repository, with optional *output-based* duplicate detection
2. **Browser (Textual TUI)** — preview `.flf`/`.tlf` fonts live, filter, and save previews; supports fonts packaged inside ZIP archives
> Built with [Rich](https://github.com/Textualize/rich) / [Textual](https://github.com/Textualize/textual).
> Rendering uses system `figlet` (and optionally `toilet` for `.tlf`).
---
## Features at a glance
**Curator — `figlet_font_curator.py`**
* Imports `.flf`, `.tlf`, and `.flc` files from `--in` into `--out`
* **Content-based de-duplication** using fast `xxhash`
* **Optional output-based de-duplication** (`--compare-output`): hashes the **rendered FIGlet output** for a fixed sample string to detect duplicates that produce identical text output
* **Versioned name collisions**: `name.ext` → `name_v02.ext`, `name_v03.ext`, …
* Optional **recursive** scan and **structure preservation** (`--maintain-structure` with `--outsub` + `-r`)
* **Rich** progress bars and pretty console logs
* **JSONL** audit log saved to `--out`
**Browser — `figlet_font_browser.py`**
* Recursively scans a font dir for `.flf` and `.tlf`
* **ZIP support**: transparently previews `.flf`/`.tlf` found *inside* `.zip` files (cached extraction)
* Live preview of your text (adjustable width)
* Optional **FIGlet charmap** (`.flc`) via `--flc` (passed to `figlet` with `-C`)
* Optional `--use-toilet` for `.tlf` rendering
* Filter/search list; save **current** or **all** previews
* Robust output normalization: CP437/Latin-1 fallbacks, ANSI repair, backspace overstrike handling
---
## Install
### System requirements
* Python 3.9+ (tested up to 3.13)
* `figlet` in your `PATH`
* Optionally `toilet` (only if you plan to render `.tlf` with `--use-toilet`)
### Python dependencies
```bash
# in your virtualenv
pip install rich textual xxhash
```
---
## 1) Curator (CLI)
**Script:** `figlet_font_curator.py`
Imports `.flf`, `.tlf`, and `.flc` from `--in` to `--out` with de-duplication and optional organization.
Duplicates are detected either by **raw file content** (fast, default), or by **rendered output** when `--compare-output` is set.
### Usage
```bash
python figlet_font_curator.py \
--in /path/to/in_fonts \
--out /path/to/out_fonts \
[-r|--recursive] \
[--outsub source_name] \
[--maintain-structure] \
[--compare-output]
```
### Options
* `--in, -i `
Input directory containing `.flf` / `.tlf` / `.flc`
* `--out, -o `
Output directory (the master repo)
* `-r, --recursive`
Recurse into `--in` subdirectories
* `--outsub `
Place this run’s copies under `--out//` (still de-dups against **all of `--out`**)
* `--maintain-structure`
Effective **only** with `--outsub` **and** `--recursive`; preserves the input’s subdirectory layout under `--out//`
* `--compare-output`
Compare fonts by the **FIGlet-rendered output** for a fixed sample string (`"FIGLET FONT CURATOR"`) instead of raw file content
*(Requires `figlet` to be installed.)*
### How de-duplication works
* **Default (content-based):** A fast `xxhash` digest of the file bytes is used. If a match is found anywhere under `--out` (scanned recursively), the file is skipped.
* **`--compare-output`:** The Curator runs `figlet` with `-d -f ` using a fixed sample string, and hashes the resulting text. Files that produce **identical output** are treated as duplicates even if their bytes differ.
* This is useful to collapse near-identical `.flf` files that render the same.
* **Important:** Output comparison applies to **`.flf` fonts only**. Non-FIGlet files like `.tlf` or `.flc` **can’t** be rendered by `figlet`; those will be **skipped** (logged as `ERROR_FINGERPRINT`) when `--compare-output` is enabled. If your set includes `.tlf`/`.flc`, prefer the default content-based mode.
### Naming & versioning
If a filename collision occurs (same name, different content/output), Curator writes the next available versioned name in the destination folder:
```
font.flf → font_v02.flf → font_v03.flf → ...
```
### Logging
* A JSONL audit log is written to:
```
--out/figlet_import_YYYYMMDD_HHMMSS.log.jsonl
```
Each event includes timestamps, inputs, destination, fingerprint method, and action (`COPY`, `COPY_RENAMED`, `SKIP_DUPLICATE`, errors, etc.).
* Console progress and summaries use **Rich**.
### Examples
```bash
# Simple flat import (content-based de-dup)
python figlet_font_curator.py --in ./fonts_in --out ./fonts_repo
# Recursive import grouped under 'dump_2025_10_03' and preserve input tree
python figlet_font_curator.py --in ./dump --out ./repo -r --outsub dump_2025_10_03 --maintain-structure
# Output-based duplicate detection (FIGlet-rendered equivalence), .flf-only
python figlet_font_curator.py --in ./flf_only --out ./repo --compare-output
```
**Notes & caveats**
* If `--out` is nested inside `--in` and you pass `-r`, Curator automatically **excludes** the `--out` subtree to avoid re-ingesting its own output.
* `--compare-output` uses a fixed sample string (`"FIGLET FONT CURATOR"`). To change the sample text, edit `DEFAULT_COMPARE_TEXT` in the script.
---
## 2) Browser (Textual TUI)
**Script:** `figlet_font_browser.py`
A terminal UI for browsing and previewing FIGlet fonts. It recursively scans a font directory for `.flf` and `.tlf` files, **including those inside ZIP archives**, renders a live preview of your text, and (optionally) applies a FIGlet charmap (`.flc`) via `--flc`. You can filter, page through results, and save outputs.
### Usage
```bash
python figlet_font_browser.py \
--font-dir ~/figlet-fonts \
--text "Hello World" \
[--width 80] \
[--flc /path/to/latin1.flc] \
[--out-dir ./out] \
[--out-prefix run_] \
[--use-toilet]
```
### Features
* Recursively scans `--font-dir` for:
* `.flf` (FIGlet fonts)
* `.tlf` (TOIlet fonts)
* **Inside ZIPs**: `.flf`/`.tlf` files are extracted to a temp cache and previewed seamlessly
* `--flc`: pass a **FIGlet charmap** via `-C` (FIGlet-only; ignored for `toilet`)
* Live preview with adjustable width (`w`)
* Quick filter/search of font list (`/`)
* Save the current preview (`s`) or **all** filtered previews (`a`)
* Output normalization:
* Fixes ANSI sequences that get split by FIGlet’s wrapping
* Applies backspace overstrikes (for classic shading/bold tricks)
* Decodes with UTF-8, CP437, and Latin-1 fallbacks to preserve block characters
### CLI Options
* `--font-dir `: Base directory to scan recursively (default: `~/figlet-fonts`)
* `--text ` **(required)**: The text to render
* `--width `: Render width (default: TTY width or `80`)
* `--flc `: Path to a `.flc` charmap (FIGlet-only; `toilet` doesn’t support `-C`)
* `--out-dir `: Where to save previews (`s` / `a` actions)
* `--out-prefix `: Filename prefix when saving
* `--use-toilet`: Use `toilet` to render `.tlf` (if installed)
### Keybindings
* `/` — focus filter input
* `w` — change width (type a number, press Enter)
* `j` / `k` or ↓ / ↑ — move selection
* `s` — save **current** preview to `--out-dir/N.asc`
* `a` — save **all** filtered previews (with headers) to `--out-dir`
* `r` — rescan fonts
* `q` — quit
### Examples
```bash
# Browse a collection (recursively)
python figlet_font_browser.py --font-dir ~/figlet-fonts --text JSON
# Apply a specific charmap (FIGlet-only)
python figlet_font_browser.py --font-dir ~/figlet-fonts --text "Café déjà vu" \
--flc ~/figlet-fonts/charmaps/latin1.flc
# Prefer 'toilet' for .tlf fonts
python figlet_font_browser.py --font-dir ~/figlet-fonts --text "HELLO" --use-toilet
# Save previews as you browse
python figlet_font_browser.py --font-dir ~/figlet-fonts --text "Hello" \
--out-dir ./out --out-prefix run_
```
---
## Recommended workflow
1. **Curate**
Use the Curator to gather fonts into a de-duplicated repo at `--out`, optionally grouping each run with `--outsub` and mirroring structure with `--maintain-structure`.
2. **Browse**
Point the Browser at your curated repo (or any folder/ZIP collection), try `.flc` charmaps, and export previews to share or compare.
---
## Troubleshooting
**Curator**
* *“It’s ingesting its own output”* → If `--out` is under `--in` and you used `-r`, the Curator excludes `--out` automatically.
* *“Why are `.tlf` / `.flc` skipped in `--compare-output` mode?”* → Output-comparison renders with `figlet` and is intended for `.flf`; non-FIGlet files can’t be rendered there and are skipped (with error logs). Use the default content-based mode for mixed sets.
**Browser**
* *Charmap not applied* → `--flc` only applies to `.flf` (FIGlet); `toilet` does not support `-C`.
* *Garbled blocks/ANSI noise* → The app repairs split ANSI, handles backspace overstrikes, and tries CP437/Latin-1 if UTF-8 fails. If a specific font still looks off, try a different width.
---
## Project structure (suggested)
```
.
├─ figlet_font_curator.py # CLI import/dedupe (content or output-based)
├─ figlet_font_browser.py # Textual TUI w/ ZIP support
├─ README.md
└─ requirements.txt # rich, textual, xxhash
```
---
## Contributing
* Code style: Python with **2-space** indents (project preference).
* Keep CLI (Curator) and TUI (Browser) concerns decoupled.
* PRs welcome for:
* Making the Curator’s sample text configurable at runtime
* Optional watch mode for Curator (e.g., `watchfiles`)
* Fuzzy/regex filter in the Browser
* Export presets, batch selection, or side-by-side comparisons in the Browser
---
## License
MIT.