Skip to content

eea/pdf2md

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

29 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

pdf2md

Standalone multi-format PDF converter — detect figures and tables, then convert to Quarto .qmd, Markdown .md, or GitHub-Flavored Markdown .gfm.

Extracted from CLMS_documents.

Authors

  • Maciej Dudek
  • Matteo Mattiuzzi

Copyright © 2026 European Union. Licensed under EUPL-1.2.

Quick start

# Install
pip install -e .

# One-time setup (API key + default model)
python3 pdf2md.py --setup

# Convert a PDF
python3 pdf2md.py document.pdf

# Batch convert a directory
python3 pdf2md.py inbox/ --out output/

# Output format
python3 pdf2md.py document.pdf --format gfm    # GitHub-Flavored Markdown
python3 pdf2md.py document.pdf --format md     # Plain Markdown
python3 pdf2md.py document.pdf --render        # Also render to PDF via Quarto

# Use a YAML frontmatter template (with --format qmd or gfm)
python3 pdf2md.py document.pdf --template path/to/template.qmd
python3 pdf2md.py document.pdf --template https://raw.githubusercontent.com/org/repo/main/template.qmd

When --template is used, the template's YAML frontmatter block is injected into the conversion prompt. The LLM fills in document-specific values (title, date, etc.) while preserving the template's field set, order, and structure. This ensures every converted document starts with a consistent, pre-defined header.

Pipeline

  1. Phase 1 — Detect: Extract figures/tables, strip chrome, detect cover metadata
  2. Phase 2 — Convert: LLM transforms the placeholdered PDF to structured markdown
  3. Phase 2.5 — Rescue: Deterministically resolve leftover figure tokens + LLM-driven insertion of unreferenced figures
  4. Phase 3 — Tablefix: Deterministic table width, caption, and orientation fixes
  5. Phase 4 — Verify: Content-fidelity check (text coverage, figure placement, table coverage)

Configuration

API key and default model are stored in ~/.pdf2md/:

  • key — OpenRouter API key (mode 600)
  • config.json — model selection + auto-cached model limits from OpenRouter API
python3 pdf2md.py --setup    # Interactive configuration

Model Selection

Not all models work equally well for PDF conversion. Before switching models, run the bundled edge-case benchmark to see how a candidate model handles tricky content:

python3 pdf2md.py src/pdf2md/tests/fixtures/pdf2md_edgecases.pdf \
    --out /tmp/benchmark-out/ \
    --model <model-slug> \
    --postfix 0

Then check verify_report.md — the document is designed to surface failures in:

  • Table detection: text-based tables with merged cells, plus rasterized PNG tables
  • Figure extraction: embedded charts, wrapped figures, subfigures
  • Text fidelity: soft hyphens, ligatures, Unicode, intra-word formatting changes
  • Structure: nested lists, definition lists, blockquotes, task lists, two-column layout
  • Scientific content: chemical formulas, subscripts/superscripts, display equations
  • Metadata: YAML frontmatter generation (title detection, date parsing)

verify=fail on figure placement. Stick to multimodal models.

Updating

cd pdf2md
git pull

The editable install (pip install -e .) picks up changes automatically — no need to re-install unless new dependencies were added. If the pull adds new packages, run pip install -e . again to install them.

License

EUPL-1.2 — see LICENSE.

About

Standalone multi-format PDF converter (QMD, MD, GFM) — two-pass LLM pipeline with figure detection, table extraction, and content-fidelity verification

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages