Skip to main content

mdextract

Universal document → Markdown parser for AI pipelines.

Converts PDF, DOCX, XLSX, and CSV files into clean Markdown strings with a single function call. Designed to be the extraction layer in RAG systems, LLM pipelines, and document processing workflows.

import mdextract

text = mdextract.parse_file("quarterly_report.pdf")
response = llm.chat(f"Summarise this:\n\n{text}")

Features

Format Output
.pdf Markdown with headings (detected by font size) and GFM tables
.docx Markdown preserving Word heading styles (Heading 1–6, Title) and tables
.xlsx One # Sheet Name section + GFM table per worksheet
.csv Single GFM Markdown table
  • Zero configuration — just point it at a file
  • Returns a string — no temp files, no disk I/O required
  • Layout-aware for PDFs — tables are detected and rendered separately from body text; headings are inferred from font size
  • Scanned PDF / OCR support — image-only pages are automatically processed with Tesseract; supports any language and tessdata_best high-accuracy models
  • AI-pipeline friendly — output is plain UTF-8 Markdown, ready for chunking, embedding, or prompt injection

Installation

pip install mdextract

Or with uv:

uv add mdextract

Quickstart

import mdextract

# Any supported format — auto-detected from extension
text: str = mdextract.parse_file("report.pdf")
text: str = mdextract.parse_file("data.xlsx")
text: str = mdextract.parse_file("table.csv")
text: str = mdextract.parse_file("document.docx")

# Scanned / image-only PDF — French
text: str = mdextract.parse_file("rapport.pdf", ocr_lang="fra")

# Scanned PDF — French + English mixed, using high-accuracy models
BEST = r"C:\Program Files\Tesseract-OCR\tessdata_best"
text: str = mdextract.parse_file("rapport.pdf", ocr_lang="fra", tessdata_dir=BEST)

Per-format helpers

from mdextract import parse_pdf, parse_docx, parse_csv, parse_xlsx

text = parse_pdf("report.pdf")
text = parse_docx("contract.docx")
text = parse_csv("users.csv")
text = parse_xlsx("financials.xlsx")

Class API

Useful when you want to reuse an instance or save output to disk:

from mdextract import mdextract

parser = mdextract()

# Returns Markdown string
text = parser.parse_file("report.pdf")

# Also write to disk
text = parser.parse_file("report.pdf", output="report.md")

# Inspect supported formats
print(parser.supported_extensions)
# ['.csv', '.docx', '.pdf', '.xlsx']

AI Pipeline Examples

RAG (Retrieval-Augmented Generation)

import mdextract
from your_vectorstore import embed_and_store

for file in Path("docs/").glob("**/*"):
    try:
        markdown = mdextract.parse_file(str(file))
        embed_and_store(source=str(file), content=markdown)
    except ValueError:
        pass  # unsupported format, skip

LLM document Q&A

import mdextract
import openai

context = mdextract.parse_file("annual_report.pdf")

response = openai.chat.completions.create(
    model="gpt-4o",
    messages=[
        {"role": "system", "content": "You are a financial analyst."},
        {"role": "user", "content": f"Answer based on this document:\n\n{context}\n\nQuestion: What was the net revenue?"},
    ],
)

Batch processing

import mdextract
from pathlib import Path

results = {}
for path in Path("uploads/").iterdir():
    try:
        results[path.name] = mdextract.parse_file(str(path))
    except (ValueError, FileNotFoundError) as e:
        results[path.name] = f"Error: {e}"

Format Notes

PDF

  • Character-level extraction via pdfplumber
  • Tables detected automatically using ruling lines; table cells excluded from body text stream
  • Headings detected by font size relative to the dominant body font size
  • Page separators inserted as --- with <!-- Page N --> comments
  • Scanned pages (no embedded text) are automatically OCR'd via Tesseract — no extra code needed

OCR parameters

Parameter Default Description
ocr_lang "eng" Tesseract language code(s). Use "fra" for French, "eng+fra" for mixed.
tessdata_dir None Path to a custom tessdata folder. Point this at tessdata_best for higher accuracy.
import mdextract

# Standard English OCR (default)
text = mdextract.parse_file("scan.pdf")

# French OCR — standard models
text = mdextract.parse_file("rapport.pdf", ocr_lang="fra")

# French OCR — high-accuracy models (tessdata_best)
BEST = r"C:\Program Files\Tesseract-OCR\tessdata_best"
text = mdextract.parse_file("rapport.pdf", ocr_lang="fra", tessdata_dir=BEST)

See OCR Setup below for installation instructions.

DOCX

  • Heading levels mapped from Word's built-in styles (Heading 1 → #, Title → #, etc.)
  • List items detected via w:numPr XML nodes and rendered as - item
  • Merged table cells are handled; content is joined with a space

XLSX

  • Each worksheet becomes a top-level section: # Sheet Name
  • Fully empty rows at the end of a sheet are stripped
  • Cell values are coerced to strings; None cells become empty strings
  • Multi-sheet workbooks produce multiple sections separated by ---

CSV

  • First row treated as the header
  • UTF-8 BOM handled automatically (utf-8-sig encoding)
  • Short rows padded to match the column count of the widest row

Error Handling

import mdextract

try:
    text = mdextract.parse_file("report.pdf")
except FileNotFoundError:
    print("File does not exist")
except ValueError as e:
    print(e)  # "Unsupported file type '.xyz'. Supported: .csv, .docx, .pdf, .xlsx"

OCR Setup (Scanned PDFs)

For PDFs that contain scanned images instead of embedded text, mdextract automatically falls back to Tesseract OCR. Two extra packages and a Tesseract installation are required.

1 — Install Tesseract

Windows: Download and run the UB Mannheim installer: https://github.com/UB-Mannheim/tesseract/wiki

Default install path: C:\Program Files\Tesseract-OCR\tesseract.exe

macOS:

brew install tesseract

Linux (Debian/Ubuntu):

sudo apt install tesseract-ocr

2 — Install Python OCR dependencies

pip install pymupdf pytesseract

Or with extras (if published with OCR extras):

pip install "mdextract[ocr]"

3 — Download language models

Standard models (faster, smaller — ~5 MB each):

# Windows — save to Tesseract's tessdata folder
$dir = "C:\Program Files\Tesseract-OCR\tessdata"
Invoke-WebRequest -Uri "https://github.com/tesseract-ocr/tessdata/raw/main/fra.traineddata" -OutFile "$dir\fra.traineddata"
Invoke-WebRequest -Uri "https://github.com/tesseract-ocr/tessdata/raw/main/eng.traineddata" -OutFile "$dir\eng.traineddata"

Best models (higher accuracy, larger — ~20 MB each):

# Windows — save to a separate tessdata_best folder
$best = "C:\Program Files\Tesseract-OCR\tessdata_best"
New-Item -ItemType Directory -Path $best -Force
Invoke-WebRequest -Uri "https://github.com/tesseract-ocr/tessdata_best/raw/main/fra.traineddata" -OutFile "$best\fra.traineddata"
Invoke-WebRequest -Uri "https://github.com/tesseract-ocr/tessdata_best/raw/main/eng.traineddata" -OutFile "$best\eng.traineddata"

All available languages: https://github.com/tesseract-ocr/tessdata_best

4 — Verify

& "C:\Program Files\Tesseract-OCR\tesseract.exe" --list-langs
# Should include: eng, fra (and any others you downloaded)

Using tessdata_best in code

import mdextract

BEST = r"C:\Program Files\Tesseract-OCR\tessdata_best"

text = mdextract.parse_file(
    "rapport.pdf",
    ocr_lang="fra",          # language code
    tessdata_dir=BEST,       # point at tessdata_best folder
)

Note: Every language you pass in ocr_lang must have a matching .traineddata file in the tessdata_dir folder. For ocr_lang="fra+eng" you need both fra.traineddata and eng.traineddata.


Requirements

CSV parsing uses the Python standard library only.

Optional (scanned PDF / OCR support):


License

MIT

Metadata

Release files for mdextract 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mdextract 1.0.0
File Size Uploaded
mdextract-1.0.0.tar.gz 11.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for mdextract 1.0.0
File Interpreter ABI Platform
mdextract-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 24.6 kB

Release files / mdextract-1.0.0.tar.gz

Download URL mdextract-1.0.0.tar.gz
Size 11.1 kB
Tags Source
SHA-256 checksum
How to use checksums
a4e315dc2dd897d1636cde0683b651dfe98b0c1a1fd054a8a15ffb5818469874
BLAKE2b-256 checksum
How to use checksums
68ad7de22cf9403c01104e3be3cc946482c19b03681899b07cf5cb8f4447a492
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.10.4 {"installer":{"name":"uv","version":"0.10.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / mdextract-1.0.0-py3-none-any.whl

Download URL mdextract-1.0.0-py3-none-any.whl
Size 13.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e52bb6b72d6d27abeaf94dda21de3253bd9fcca7ef4c50029e7bd9d6c75b6698
BLAKE2b-256 checksum
How to use checksums
cef4688530aa560abf8ff36e74d4cd2a44004e012c3e6c1f0393a2e945dd8964
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.10.4 {"installer":{"name":"uv","version":"0.10.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page