- TypeScript 99.7%
- Dockerfile 0.3%
Clears the Node 20 runtime deprecation warnings: checkout v4→v6, upload-artifact v4→v7, download-artifact v4→v8. |
||
|---|---|---|
| .github/workflows | ||
| scripts | ||
| src | ||
| test | ||
| .gitignore | ||
| biome.json | ||
| bun.lock | ||
| docker-compose.yml | ||
| Dockerfile | ||
| lefthook.yml | ||
| LICENSE | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
Quran Image Generator
Warning: This project is under active development and has not been proofread against a printed Mushaf. Do not use the output for production or distribution without thorough verification.
A CLI tool that generates per-line and per-page PNG/WebP images of the Quran using QCF (Quran Complex Font) page fonts.
Black glyphs on transparent background — enabling dark/light theming via tint color in consuming apps.
Supports font versions V1 (1405H), V2 (1423H), and V4 (Tajweed with embedded colors).
Setup
bun install
Usage
Interactive
bun src/cli.ts
Prompts for version, mode, width, page range, format, and markers. Downloads assets from GitHub releases if missing.
CLI args
bun src/cli.ts [startPage] [endPage] [width] [mode] [v1|v2|v4] [no-markers] [webp] [bounds] [json] [quantize] [dark] [--ink <hex>] [--recolor <SRC=DST,...|file.json>]
Examples:
bun src/cli.ts 1 604 1440 line v2 # all pages, line mode, V2
bun src/cli.ts 1 10 1440 page v1 no-markers # pages 1-10, page mode, no markers
bun src/cli.ts 1 604 1440 line v4 webp # V4 tajweed, WebP format
Dark theme & tajweed recolor (V4 only)
V4 tajweed colors live in the font's COLR/CPAL color tables, not in fillStyle, so they're
recolored by patching CPAL palette 0 at font-registration time. Glyph geometry (glyf/hmtx)
and alpha are left byte-identical — only color records change.
bun src/cli.ts 1 604 2160 line v4 dark # base ink → light default (#E8E0D4)
bun src/cli.ts 1 604 2160 line v4 --ink "#E8E0D4" # custom base ink (implies dark)
dark/--ink <hex>— recolors the near-black base ink only; saturated tajweed rules are left as-is.--recolor "RRGGBB=RRGGBB,..."— optional, generic remap of any palette entry to a new color. Each source is matched against palette 0 with a ±10/channel tolerance. No palette is baked in — supply your own. Combine with--ink/darkto make rule colors legible on a dark background:
# Lighten the base ink and remap two tajweed rule colors in one pass:
bun src/cli.ts 1 604 2160 line v4 --ink "#E8E0D4" \
--recolor "F40000=F77B72,3F48E6=6BA6E6"
# Or supply the map as a JSON file of { "SRC": "DST" } hex pairs:
bun src/cli.ts 1 604 2160 line v4 --recolor recolor.json
Docker
docker compose build
docker compose run generator 1 604 1440 line v2
Prebuilt binary (recommended)
Each release ships self-contained binaries with assets embedded — no bun install, no font/layout download, no Cairo or unzip required. Only tar (in every Linux/macOS base) is used to unpack assets on first run.
Pick the variant for your platform from the latest release:
| Variant | Targets | Use when |
|---|---|---|
quran-gen-{platform} |
v1 + v2 + v4 | You want every font version in one binary |
quran-gen-v1-{platform} |
v1 only | Smaller download, single version |
quran-gen-v2-{platform} |
v2 only | Smaller download, single version |
quran-gen-v4-{platform} |
v4 only | Smaller download, single version |
Supported platforms: darwin-arm64 (Apple Silicon), linux-x64, linux-arm64.
# Apple Silicon (all versions)
curl -L https://github.com/NedaaDevs/quran-image-generator/releases/latest/download/quran-gen-darwin-arm64.tar.gz | tar xz
./quran-gen-darwin-arm64 1 604 1440 line v2
# Linux x64 (v2 only)
curl -L https://github.com/NedaaDevs/quran-image-generator/releases/latest/download/quran-gen-v2-linux-x64.tar.gz | tar xz
./quran-gen-v2-linux-x64 1 604 1440 line v2
The tarball contains the binary plus a pngquant sidecar; keep them next to each other.
Build from source
bun build src/cli.ts --compile --external canvas --outfile quran-gen
./quran-gen 1 604 1440 line v2
Markers mode
By default, images include ayah end markers (ornamental numbered circles) and surah header frames rendered with the page font. Use no-markers to omit decorative elements — useful for apps that overlay their own themed markers:
- With markers — full preview with ornamental ayah markers and surah header frames baked into the image
- Without markers — text only, plus version-matched surah names (no frame). Apps can overlay custom-themed markers using glyph positions from
bounds.db
The surah frame template is exported separately to markers/surah-frame.png for apps to composite with their own styling.
Output structure
output/{version}/{width}/
lines/{page}/001..015.png # per-line images (15 per page)
pages/001..604.png # full page images
markers/surah-frame.png # ornamental frame template
bounds.db # glyph bounding boxes (SQLite)
bounds.db schema
glyph_bounds
| Column | Type | Description |
|---|---|---|
| page | INTEGER | Page number (1–604) |
| line | INTEGER | Line number (1–15) |
| position | INTEGER | Word position within the line |
| surah_number | INTEGER | Surah number (1–114) |
| ayah_number | INTEGER | Ayah number within the surah |
| x | INTEGER | Left edge (px) |
| y | INTEGER | Top edge (px) |
| width | INTEGER | Glyph width (px) |
| height | INTEGER | Glyph height (px) |
| is_marker | INTEGER | 1 = ayah end marker, 0 = word |
| word_index | INTEGER | Word number within its ayah (join key: surah:ayah:word) |
| tajweed_index | TEXT | V4 tajwid rule(s): CPAL palette slot index/indices, comma-joined for multi-rule words (e.g. 15,5), joinable to tajweed_palette. NULL for V1/V2, markers, and base-ink-only words. |
The slot index is a stable rule key (it doesn't shift across font builds the way the raw RGB does); map it to a rule in your own legend. Use tajweed_palette only if you need the font's native color.
tajweed_palette
The font's canonical tajwid colors, written once (the palette is identical across all page fonts). Empty for V1/V2.
| Column | Type | Description |
|---|---|---|
| idx | INTEGER | CPAL palette slot (matches a value in tajweed_index) |
| hex | TEXT | Canonical source color #RRGGBB, uppercase |
The DB's PRAGMA user_version carries the schema version (currently 2); bump it on any schema change so clients can detect a stale download and re-fetch.
line_metadata
| Column | Type | Description |
|---|---|---|
| page | INTEGER | Page number (1–604) |
| line | INTEGER | Line number (1–15) |
| type | TEXT | text, surah-header, or basmala |
| surah_number | INTEGER | Surah number (nullable) |
| surah_name | TEXT | Arabic surah name (nullable) |
Apps can render marker-less images and overlay ayah markers at runtime by querying:
SELECT x, y, width, height, ayah_number
FROM glyph_bounds
WHERE page = ? AND is_marker = 1
Then render each marker at the given position using the ayah number.
Data
Assets are downloaded automatically via the interactive CLI, or manually from GitHub releases:
data/
common/fonts/ # surah name fonts, surah header font
{v1,v2,v4}/
quran-layout.db # page/line/word layout + surah metadata
fonts/p1..p604.ttf # per-page QCF fonts
Font versions
| Version | Edition | Layout | Notes |
|---|---|---|---|
| V1 | 1405H Madinah Mushaf | 604 pages, 15 lines | Uthman Taha calligraphy |
| V2 | 1423H Madinah Mushaf | 604 pages, 15 lines | Updated edition |
| V4 | Tajweed | Same as V1 | Embedded colors for tajweed rules |
Credits
Tarteel AI — Quranic Universal Library (QUL)
Special thanks to Tarteel AI for their Quranic Universal Library, which provides the essential data powering this tool:
- QCF font files — per-page V1, V2, and V4 glyph fonts
- Word-by-word glyph codes — mapping each Quranic word to its QCF codepoint
- Mushaf layout data — page/line/word mapping for the 1405H and 1423H Madinah Mushaf editions
- Surah name ligature fonts — version-matched calligraphic surah names
Other Credits
- quran.com-images — inspiration and original approach
- King Fahd Quran Printing Complex — the original Madinah Mushaf calligraphy by Uthman Taha
License
MIT