Generate per-line and per-page Quran images using QCF fonts — V1, V2, V4 Tajweed. Black on transparent for theming.
  • TypeScript 99.7%
  • Dockerfile 0.3%
Find a file
Fahad Alashari aa093d3281
ci: bump GitHub Actions to Node 24 majors (#1)
Clears the Node 20 runtime deprecation warnings: checkout v4→v6,
upload-artifact v4→v7, download-artifact v4→v8.
2026-06-16 16:13:12 +03:00
.github/workflows ci: bump GitHub Actions to Node 24 majors (#1) 2026-06-16 16:13:12 +03:00
scripts fix(layout): place line-spilled ayah markers at the next line's start 2026-06-05 02:06:37 +03:00
src feat(v4): add tajweed_index, word_index, and tajweed_palette to bounds.db 2026-06-16 15:05:23 +03:00
test test: cover tajweed metadata, word_index, and multi-width render invariants 2026-06-16 15:05:24 +03:00
.gitignore update gitignore for data/ and credits for QUL 2026-03-12 08:27:17 +03:00
biome.json test: add layout/render regression suite (bun test) 2026-06-05 02:31:37 +03:00
bun.lock ci(release): tolerate pngquant-bin arm64 failure, fall back to apt 2026-05-13 19:26:21 +03:00
docker-compose.yml feat: add Docker support 2026-03-14 01:11:23 +03:00
Dockerfile refactor: distroless Docker image with compiled binary 2026-03-14 01:15:50 +03:00
lefthook.yml chore: tolerate biome-ignored files in pre-commit hook 2026-05-13 17:53:30 +03:00
LICENSE chore: add MIT license 2026-03-14 02:00:32 +03:00
package.json test: add layout/render regression suite (bun test) 2026-06-05 02:31:37 +03:00
README.md docs: document tajweed_index, tajweed_palette, and word_index 2026-06-16 15:05:25 +03:00
tsconfig.json init project scaffolding 2026-03-12 07:35:47 +03:00

Quran Image Generator

CI Release Assets

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/dark to 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

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 (1604)
line INTEGER Line number (115)
position INTEGER Word position within the line
surah_number INTEGER Surah number (1114)
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 (1604)
line INTEGER Line number (115)
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

License

MIT