Skip to content

Macowen14/doc2image

Repository files navigation

Doc2Image

Convert any document into a visually stunning image — structure preserved, code highlighted, tables rendered pixel-perfect.

Python License: MIT Tests Coverage

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.


Table of Contents


Features

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

Demo Output

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
done

Architecture

Doc2Image 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.


Installation

Requirements

  • Python 3.12+
  • uv package manager

Steps

# 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

What gets installed

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

Bundled fonts

JetBrains Mono and Fira Code are bundled in the fonts/ directory and are automatically used for code blocks without any system configuration needed.


Quick Start

# 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 github

Output files are written to output/ by default:

output/
├── page_001.png
├── page_002.png   ← only if content exceeds max page height
└── page_003.png

CLI Reference

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.

Common examples

# 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 --verbose

Themes

Doc2Image ships with 5 carefully designed themes:

github (default)

Matches GitHub.com's Markdown rendering exactly.

  • Background: #ffffff white
  • Text: #24292e dark charcoal
  • Headings: #24292e with #0366d6 blue accent bars
  • Code: #f6f8fa light grey background, github-light Pygments style
  • Tables: #f6f8fa header, #dfe2e5 border

light

Clean minimal light theme — great for documents and reports.

  • Background: #ffffff
  • Code: friendly Pygments style
  • Accent: #4f7ec9 steel blue

dark

Catppuccin Mocha — a popular, easy-on-the-eyes dark palette.

  • Background: #1e1e2e deep navy
  • Text: #cdd6f4 lavender
  • Headings: #cba6f7 purple
  • Code: #181825 background, Monokai highlighting
  • Accents: #89b4fa blue

notion

Inspired by the Notion note-taking app — wide margins, spacious layout.

  • Background: #ffffff
  • Heading accent: #eb5757 coral red
  • Wider margins: 96px (vs 60px standard)
  • Code: pastie Pygments style

terminal

Phosphor CRT terminal aesthetic — all monospace, green on black.

  • Background: #0d0d0d near-black
  • Text: #33ff33 phosphor green
  • Headings: #00ff88 bright green
  • Accents: #ff6600 orange
  • Code: Monokai on #0a0a0a
  • Font: JetBrains Mono for all text

Custom Themes

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

Supported Input Formats

Extension Format Notes
.pdf 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.


Optional — Ollama AI

Doc2Image optionally integrates with Ollama to enhance document quality before rendering.

Setup

# 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

Supported models

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

Usage

# 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-coder

Graceful fallback

If 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.

Logging

Doc2Image uses Python's standard logging module with Rich formatting. Every pipeline stage logs its result with timestamps and source file references.

Default (INFO level)

[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)

Debug level (--verbose)

[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)

Environment variable

export DOC2IMAGE_LOG_LEVEL=DEBUG
uv run python -m doc2image.cli convert report.pdf

Project Structure

image-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

Running Tests

# 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=DEBUG

Test results

78 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%

Environment Variables

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

License

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.

Acknowledgements

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

About

No description, website, or topics provided.

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages