PyMuPDF4LLM

Markdown-conversion layer built on PyMuPDF: converts PDF pages to Markdown while preserving headings, tables (ruled-line or text-position-based detection), and images, specifically tuned for feeding PDF content to LLMs as clean, structured Markdown rather than raw extracted text. A sibling package to base PyMuPDF rather than a separate engine — it reuses PyMuPDF's own C-level parsing and adds the Markdown-shaping layer on top.

Sample Results

Real output from running this tool against a sample PDF, as part of PDFog's open-source PDF tooling benchmark.

Input: sample-brochure.pdf

Loading PDF…

table_strategy="lines_strict" (the library default): requires actual ruling lines to detect a table, same idea as tabula-java's -l/--lattice.

uv run python3 ./tests/pymupdf4llm_extract.py "sample-brochure.pdf" "sample-brochure--pymupdf4llm-python.md" lines_strict

Output: sample-brochure--pymupdf4llm-python.md



<!-- Start of picture text -->
REAL ESTATE<br><!-- End of picture text -->

# **221 Queen Street, Melbourne VIC 3000** 

apartment an **2 1 1** 

$1,000,000 

## **Prince Cascading** 

1234-5678 

## **Additional Information** 

Land Size 1171m2 House Size - , - =ag ay. a: Map Ref. 4 Energy Rating - vat 4 _— 4 Council Rates - \ \ Vy Water Rates - 



<!-- Start of picture text -->
mS r<br>~<br>-<br>if ¥ t a 7. a.<br>i ’ | p- 2, i<br>) Biscay <- iema “4 aed=<br>4} . , mate, SS |<br>w no \ : i :LN BS<br><!-- End of picture text -->



<!-- Start of picture text -->
% rane . nor ar<br>Ne 8 Central<br>acon St Francis i g<br>gstatt &) Catholic Church"!<br>ose 8<br>:<br>Lae i=} s Me<br>goutte = gourt®<br>3 une? Bg<br>o Ly<br><!-- End of picture text -->

## **WARM BEAUTIFUL APARTMENT** 

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis ante, fermentum ac cursus vitae, tempus vel ipsum. Proin justo lorem, mattis sed pharetra ac, vehicula tincidunt sem. Mauris est leo, scelerisque at gravida vel, interdum quis felis. Nullam nibh magna, consectetur eu aliquet ac, sollicitudin eget nisl. Praesent nibh tellus, elementum ut scelerisque a, pretium eget libero. Morbi sodales tincidunt turpis et sagittis. Aenean sed dictum nisi. 

Phasellus quis metus lectus. Donec varius pellentesque purus, et ornare enim pellentesque sed. Morbi quam augue, pulvinar sit amet mollis in, euismod porta orci. Quisque rutrum egestas enim ac viverra. Mauris blandit tristique feugiat. Ut molestie molestie turpis sed elementum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas in nunc odio. Nunc sagittis, mi ut ornare pellentesque, orci augue dapibus est, vel rutrum elit orci vel augue. 

table_strategy="text": text-position-based heuristic instead of ruling lines, same idea as tabula-java's default -g/--guess — a genuinely different detection algorithm, not just a formatting tweak.

uv run python3 ./tests/pymupdf4llm_extract.py "sample-brochure.pdf" "sample-brochure--pymupdf4llm-python--table-strategy-text.md" text

Output: sample-brochure--pymupdf4llm-python--table-strategy-text.md



<!-- Start of picture text -->
REAL ESTATE<br><!-- End of picture text -->

# **221 Queen Street, Melbourne VIC 3000** 

apartment an **2 1 1** 

$1,000,000 

## **Prince Cascading** 

1234-5678 

## **Additional Information** 

Land Size 1171m2 House Size - , - =ag ay. a: Map Ref. 4 Energy Rating - vat 4 _— 4 Council Rates - \ \ Vy Water Rates - 



<!-- Start of picture text -->
mS r<br>~<br>-<br>if ¥ t a 7. a.<br>i ’ | p- 2, i<br>) Biscay <- iema “4 aed=<br>4} . , mate, SS |<br>w no \ : i :LN BS<br><!-- End of picture text -->



<!-- Start of picture text -->
% rane . nor ar<br>Ne 8 Central<br>acon St Francis i g<br>gstatt &) Catholic Church"!<br>ose 8<br>:<br>Lae i=} s Me<br>goutte = gourt®<br>3 une? Bg<br>o Ly<br><!-- End of picture text -->

## **WARM BEAUTIFUL APARTMENT** 

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis ante, fermentum ac cursus vitae, tempus vel ipsum. Proin justo lorem, mattis sed pharetra ac, vehicula tincidunt sem. Mauris est leo, scelerisque at gravida vel, interdum quis felis. Nullam nibh magna, consectetur eu aliquet ac, sollicitudin eget nisl. Praesent nibh tellus, elementum ut scelerisque a, pretium eget libero. Morbi sodales tincidunt turpis et sagittis. Aenean sed dictum nisi. 

Phasellus quis metus lectus. Donec varius pellentesque purus, et ornare enim pellentesque sed. Morbi quam augue, pulvinar sit amet mollis in, euismod porta orci. Quisque rutrum egestas enim ac viverra. Mauris blandit tristique feugiat. Ut molestie molestie turpis sed elementum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas in nunc odio. Nunc sagittis, mi ut ornare pellentesque, orci augue dapibus est, vel rutrum elit orci vel augue.