Convert any document into a visually stunning image — structure preserved, code highlighted, tables rendered pixel-perfect.
Doc2Image is a Python application that converts documents into professional-quality PNG, JPEG, or WebP images. It preserves document structure — headings, paragraphs, lists, tables, and syntax-highlighted code blocks — and renders them using a layout engine built on Pillow and OpenCV, with 5 beautiful built-in themes.
- Features
- Demo Output
- Architecture
- Installation
- Quick Start
- CLI Reference
- Themes
- Custom Themes
- Supported Input Formats
- Optional — Ollama AI
- Logging
- Project Structure
- Running Tests
- Environment Variables
- License
| Feature | Description |
|---|---|
| 📄 Multi-format input | PDF, DOCX, PPTX, XLSX, HTML, TXT, Markdown — powered by MarkItDown |
| 🧩 Structured rendering | Headings (H1–H6), paragraphs, lists, tables, code, blockquotes, HR |
| 🎨 Syntax highlighting | Pygments-powered with JetBrains Mono and Fira Code fonts bundled |
| 🖥 5 built-in themes | Light, Dark (Catppuccin), GitHub, Notion, Terminal (phosphor) |
| 📐 Layout engine | Automatic text wrapping, margin/padding control, dynamic image height |
| 📑 Pagination | Long documents split into page_001.png, page_002.png, … |
| 🤖 Optional Ollama AI | Cleanup OCR artifacts or summarize documents with llama3.2:3b / qwen2.5-coder |
| 📦 OpenCV export | Compression control, scale 0.1×–4.0×, PNG / JPEG / WebP |
| 🔧 Custom themes | YAML-configurable colors, fonts, spacing, and code highlighting style |
| 📊 Type-safe | Full Pydantic models, type hints throughout, 75% test coverage |
| 🪵 Structured logging | Rich-formatted live logs at every pipeline stage |
All output generated from examples/sample.md:
| Theme | Description | Output |
|---|---|---|
| github | GitHub.com markdown style | output/github/page_001.png |
| dark | Catppuccin Mocha — dark palette | output/dark/page_001.png |
| notion | Wide margins, Notion aesthetic | output/notion/page_001.png |
| terminal | Phosphor green-on-black CRT | output/terminal/page_001.png |
| light | Clean minimal white | output/light/page_001.png |
Generate all demo outputs:
for theme in github dark notion terminal light; do
uv run python -m doc2image.cli convert examples/sample.md \
--theme $theme --output-dir output/$theme
doneDoc2Image processes documents through a clean, layered pipeline:
Input File
│
▼
MarkItDown ──────────────────────── Ingestion Layer
│ Extracts text from PDF/DOCX/PPTX/XLSX/HTML/TXT/MD
│
▼
markdown-it-py ──────────────────── Parsing Layer
│ Converts Markdown to typed blocks:
│ HeadingBlock · ParagraphBlock · ListBlock
│ TableBlock · CodeBlock · QuoteBlock · HRuleBlock
│
▼
[Ollama] (optional) ─────────────── AI Layer
│ Cleanup OCR artifacts or summarize content
│
▼
LayoutEngine ────────────────────── Layout Layer
│ Measures block heights, wraps text, paginates
│ → List[Page] with PositionedBlock objects
│
▼
PillowRenderer ──────────────────── Rendering Layer
│ Draws each block to PIL Image canvas
│ Dispatches to CodeRenderer / TableRenderer
│
▼
OpenCV ──────────────────────────── Export Layer
│ Compression, resize, format conversion
│
▼
page_001.png page_002.png page_003.png ...
See ARCHITECTURE.md for full Mermaid diagrams of every layer, data model, state machine, and sequence diagram.
- Python 3.12+
- uv package manager
# Clone the repository
git clone https://github.com/Macowen14/doc2image.git
cd doc2image
# Install all dependencies using uv
uv sync
# Install the package in editable mode
uv pip install -e .
# Verify installation
uv run python -m doc2image.cli --help| Package | Version | Purpose |
|---|---|---|
markitdown[all] |
≥0.1.0 | Document ingestion (PDF, DOCX, PPTX, XLSX, HTML) |
markdown-it-py |
≥4.0.0 | Markdown parsing to token AST |
pillow |
≥12.0.0 | Image rendering canvas |
pygments |
≥2.18.0 | Syntax highlighting for code blocks |
opencv-python |
≥4.0.0 | Image export, compression, resizing |
typer |
≥0.9.0 | CLI framework |
rich |
≥13.0.0 | Beautiful console output and logging |
pydantic |
≥2.0.0 | Type-safe configuration models |
pydantic-settings |
≥2.0.0 | Environment variable overrides |
pyyaml |
≥6.0 | YAML theme loading |
pandas, numpy |
latest | Data processing support |
requests |
≥2.28 | Ollama API communication |
pytest, pytest-cov |
latest | Testing and coverage |
JetBrains Mono and Fira Code are bundled in the fonts/ directory and are automatically used for code blocks without any system configuration needed.
# Convert a Markdown file (uses github theme by default)
uv run python -m doc2image.cli convert examples/sample.md
# Convert a PDF
uv run python -m doc2image.cli convert report.pdf
# Convert a Word document with dark theme
uv run python -m doc2image.cli convert document.docx --theme dark
# Convert a spreadsheet to images
uv run python -m doc2image.cli convert data.xlsx --theme notion
# Convert a PowerPoint presentation
uv run python -m doc2image.cli convert slides.pptx --theme light
# Convert HTML page
uv run python -m doc2image.cli convert page.html --theme githubOutput files are written to output/ by default:
output/
├── page_001.png
├── page_002.png ← only if content exceeds max page height
└── page_003.png
Usage: python -m doc2image.cli convert [OPTIONS] INPUT_FILE
Convert a document into a series of image files.
Arguments:
INPUT_FILE Path to the input document [required]
Options:
-t, --theme TEXT Theme: light|dark|github|notion|terminal|path.yaml [default: github]
-o, --output-dir PATH Directory to write output images [default: output]
-f, --format TEXT Output format: png, jpeg, webp [default: png]
-q, --quality INT JPEG/WebP compression quality 1-100 [default: 90]
-s, --scale FLOAT Image scale factor 0.1-4.0 [default: 1.0]
--summarize Summarize document with Ollama before rendering
--cleanup Clean up OCR artifacts with Ollama before rendering
--model TEXT Ollama model for summarize/cleanup [default: llama3.2:3b]
--ollama-host TEXT Ollama API host [default: http://localhost:11434]
--line-numbers Show line numbers in code blocks
--max-height INT Maximum page height in pixels [default: 16384]
-v, --verbose Enable debug-level logging
--list-themes List available themes and exit
--help Show this message and exit.
# Use a dark theme with JPEG output at 90% quality
uv run python -m doc2image.cli convert report.pdf \
--theme dark --format jpeg --quality 90
# Generate a 2x resolution PNG with line numbers on code blocks
uv run python -m doc2image.cli convert notes.md \
--scale 2.0 --line-numbers
# Summarize a long PDF and render as WebP
uv run python -m doc2image.cli convert annual_report.pdf \
--summarize --model llama3.2:3b --format webp --output-dir ./report-images
# Clean up OCR artifacts from a scanned PDF
uv run python -m doc2image.cli convert scanned.pdf \
--cleanup --model qwen2.5-coder
# Use a custom theme file
uv run python -m doc2image.cli convert notes.md \
--theme ./my-custom-theme.yaml
# List all available built-in themes
uv run python -m doc2image.cli themes
# Enable verbose debug logging to trace every block and font
uv run python -m doc2image.cli convert report.pdf --verboseDoc2Image ships with 5 carefully designed themes:
Matches GitHub.com's Markdown rendering exactly.
- Background:
#ffffffwhite - Text:
#24292edark charcoal - Headings:
#24292ewith#0366d6blue accent bars - Code:
#f6f8falight grey background,github-lightPygments style - Tables:
#f6f8faheader,#dfe2e5border
Clean minimal light theme — great for documents and reports.
- Background:
#ffffff - Code:
friendlyPygments style - Accent:
#4f7ec9steel blue
Catppuccin Mocha — a popular, easy-on-the-eyes dark palette.
- Background:
#1e1e2edeep navy - Text:
#cdd6f4lavender - Headings:
#cba6f7purple - Code:
#181825background, Monokai highlighting - Accents:
#89b4fablue
Inspired by the Notion note-taking app — wide margins, spacious layout.
- Background:
#ffffff - Heading accent:
#eb5757coral red - Wider margins: 96px (vs 60px standard)
- Code:
pastiePygments style
Phosphor CRT terminal aesthetic — all monospace, green on black.
- Background:
#0d0d0dnear-black - Text:
#33ff33phosphor green - Headings:
#00ff88bright green - Accents:
#ff6600orange - Code: Monokai on
#0a0a0a - Font: JetBrains Mono for all text
Create a .yaml file with any of the following keys:
# my-theme.yaml
name: my-theme
page_width: 1200 # Total image width in pixels
margin: 60 # Left/right outer margin
padding: 20 # Inner padding for code/quote blocks
line_height: 1.6 # Body text line height multiplier
paragraph_spacing: 16 # Pixels between paragraphs
heading_spacing_before: 24
heading_spacing_after: 12
code_padding: 16 # Padding inside code block background
table_cell_padding: 10
code_theme: monokai # Any Pygments style name
fonts:
body: { family: DejaVuSans, size: 16 }
heading1: { family: DejaVuSans, size: 36, bold: true }
heading2: { family: DejaVuSans, size: 28, bold: true }
heading3: { family: DejaVuSans, size: 22, bold: true }
code: { family: JetBrainsMono, size: 14 }
quote: { family: DejaVuSans, size: 15, italic: true }
table_header: { family: DejaVuSans, size: 15, bold: true }
colors:
background: "#1a1a2e"
text: "#eaeaea"
heading: "#00d4ff"
heading_accent: "#ff6b35"
code_bg: "#0d0d1a"
code_text: "#e0e0e0"
code_border: "#2a2a3e"
table_header_bg: "#2a2a3e"
table_header_text: "#00d4ff"
table_border: "#3a3a4e"
table_row_alt: "#141428"
quote_bg: "#0d0d1a"
quote_border: "#ff6b35"
quote_text: "#a0a0b0"
hr: "#2a2a3e"
link: "#00aaff"
list_bullet: "#ff6b35"Available Pygments code theme names include: monokai, friendly, github-light, pastie, default, dracula, one-dark, nord, solarized-dark, solarized-light, and many more.
# Use your custom theme
uv run python -m doc2image.cli convert report.pdf --theme ./my-theme.yaml| Extension | Format | Notes |
|---|---|---|
.pdf |
Text extraction via pdfminer + pdfplumber | |
.docx |
Word Document | Full paragraph/table/heading support |
.pptx |
PowerPoint | Slide text extraction |
.xlsx |
Excel Spreadsheet | Sheet/cell content as markdown tables |
.html, .htm |
HTML | BeautifulSoup extraction |
.txt |
Plain Text | Passed through as-is |
.md |
Markdown | Passed through directly |
.csv |
CSV | Converted to markdown table |
.json |
JSON | Formatted and wrapped |
.xml |
XML | Text content extracted |
All formats are handled by MarkItDown, which outputs clean Markdown that is then parsed by markdown-it-py.
Doc2Image optionally integrates with Ollama to enhance document quality before rendering.
# Install Ollama
curl -fsSL https://ollama.ai/install.sh | sh
# Pull a supported model
ollama pull llama3.2:3b
# or
ollama pull qwen2.5-coder
# Start the Ollama server
ollama serve| Model | Best for | Size |
|---|---|---|
llama3.2:3b |
General cleanup and summarization | ~2 GB |
qwen2.5-coder |
Technical documents with code | ~1.5 GB |
llama3.2 |
High-quality summarization | ~4 GB |
mistral |
Fast general purpose | ~4 GB |
# Clean up OCR artifacts from a scanned document
uv run python -m doc2image.cli convert scanned.pdf --cleanup
# Summarize a long report before rendering
uv run python -m doc2image.cli convert long_report.pdf --summarize
# Both cleanup and summarize
uv run python -m doc2image.cli convert document.pdf --cleanup --summarize
# Use a specific model
uv run python -m doc2image.cli convert report.pdf --summarize --model qwen2.5-coderIf Ollama is not running, Doc2Image logs a warning and continues with the original text — it never crashes due to Ollama being unavailable:
⚠ Ollama not reachable at http://localhost:11434 — skipping cleanup.
Start Ollama with `ollama serve` to enable this feature.
Doc2Image uses Python's standard logging module with Rich formatting. Every pipeline stage logs its result with timestamps and source file references.
[11:49:39] INFO ✓ Loaded theme github loader.py:66
[11:49:42] INFO ✓ Ingested 4147 chars of Markdown from sample.md
[11:49:43] INFO ✓ Parsed 42 blocks: 13 headings, 9 paragraphs, 6 code, 1 tables, 2 lists
[11:49:43] INFO ✓ Layout complete: 1 page(s) from 42 blocks
[11:49:48] INFO ✓ Rendered 1 page image(s)
[11:49:48] INFO ✓ Exported page_001.png (302.9 KB)
[11:49:43] DEBUG Loaded font: JetBrainsMono-Regular.ttf (size=14, bold=False)
[11:49:43] DEBUG Loaded font: DejaVuSans-Bold.ttf (size=36, bold=True)
[11:49:43] DEBUG Rendering page 1 (1200x3989)
[11:49:43] DEBUG Resized page 1 to 2400x7978 (scale=2.00)
export DOC2IMAGE_LOG_LEVEL=DEBUG
uv run python -m doc2image.cli convert report.pdfimage-maker/
│
├── main.py # Entry point
├── pyproject.toml # Project metadata + dependencies
├── README.md # This file
├── ARCHITECTURE.md # Mermaid architecture diagrams
├── LICENSE # MIT License
│
├── fonts/ # Bundled fonts
│ ├── JetBrainsMono-Regular.ttf
│ ├── JetBrainsMono-Bold.ttf
│ ├── FiraCode-Regular.ttf
│ └── FiraCode-Bold.ttf
│
├── doc2image/ # Main package
│ ├── __init__.py # Package version
│ ├── cli.py # Typer CLI with rich progress bars
│ ├── config.py # Pydantic ThemeConfig + logging setup
│ ├── ingestion.py # MarkItDown document extraction
│ ├── parser.py # Markdown → typed block AST
│ ├── ollama_processor.py # Optional Ollama cleanup/summarize
│ ├── export.py # OpenCV image export
│ │
│ ├── layout/
│ │ ├── __init__.py
│ │ ├── engine.py # Cursor-based layout + paginator
│ │ ├── blocks.py # Page / PositionedBlock / Rect
│ │ └── fonts.py # Font registry + text wrap utils
│ │
│ ├── rendering/
│ │ ├── __init__.py
│ │ ├── pillow_renderer.py # Main Pillow drawing engine
│ │ ├── code_renderer.py # Pygments per-token syntax highlighting
│ │ └── table_renderer.py # Grid table with auto-sized columns
│ │
│ └── themes/
│ ├── __init__.py
│ ├── loader.py # YAML theme loader + validator
│ ├── light.yaml
│ ├── dark.yaml
│ ├── github.yaml
│ ├── notion.yaml
│ └── terminal.yaml
│
├── tests/ # Pytest test suite
│ ├── __init__.py
│ ├── test_ingestion.py # 12 tests
│ ├── test_parser.py # 19 tests
│ ├── test_layout.py # 18 tests
│ ├── test_themes.py # 16 tests
│ └── test_rendering.py # 13 tests
│
└── examples/
└── sample.md # Demo document with all block types
# Run all tests
uv run pytest tests/ -v
# Run with coverage report
uv run pytest tests/ -v --cov=doc2image --cov-report=term-missing
# Run a specific test file
uv run pytest tests/test_parser.py -v
# Run a specific test class
uv run pytest tests/test_rendering.py::TestExport -v
# Run with full logging output
uv run pytest tests/ -v -s --log-cli-level=DEBUG78 passed in 8.54s
Module Coverage
─────────────────────────────────────────────
doc2image/rendering/pillow_renderer 98%
doc2image/config.py 94%
doc2image/layout/blocks.py 93%
doc2image/rendering/table_renderer 90%
doc2image/layout/engine.py 89%
doc2image/parser.py 89%
doc2image/ingestion.py 89%
doc2image/themes/loader.py 88%
doc2image/export.py 83%
doc2image/layout/fonts.py 83%
─────────────────────────────────────────────
TOTAL 75%
All CLI options can be overridden with environment variables prefixed DOC2IMAGE_:
| Variable | Default | Description |
|---|---|---|
DOC2IMAGE_THEME |
github |
Default theme |
DOC2IMAGE_OUTPUT_DIR |
output |
Default output directory |
DOC2IMAGE_OUTPUT_FORMAT |
png |
Default output format |
DOC2IMAGE_JPEG_QUALITY |
90 |
Default JPEG quality |
DOC2IMAGE_SCALE |
1.0 |
Default scale factor |
DOC2IMAGE_OLLAMA_MODEL |
llama3.2:3b |
Default Ollama model |
DOC2IMAGE_OLLAMA_HOST |
http://localhost:11434 |
Ollama API URL |
DOC2IMAGE_LOG_LEVEL |
INFO |
Logging level |
DOC2IMAGE_SHOW_LINE_NUMBERS |
false |
Code line numbers |
DOC2IMAGE_MAX_IMAGE_HEIGHT |
16384 |
Max page height cap |
This project is licensed under the MIT License — see the LICENSE file for full details.
MIT License — Copyright (c) 2026 Doc2Image
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
Doc2Image is built on top of these excellent open-source projects:
- MarkItDown — Microsoft's document-to-markdown extractor
- markdown-it-py — Python port of markdown-it
- Pillow — Python Imaging Library
- Pygments — Syntax highlighting library
- OpenCV — Computer vision and image processing
- Typer — CLI framework built on Click
- Rich — Rich text and beautiful formatting
- Pydantic — Data validation with Python type hints
- JetBrains Mono — Open-source monospace font
- Fira Code — Open-source ligature monospace font
- Ollama — Local LLM inference engine