pdfmdtools.com

PDF-to-Markdown Test Corpus

Last updated: 21 September 2026

18 small PDFs, each built to break a PDF-to-Markdown converter in one specific way, plus the measured output of one converter against all of them.

The corpus is archived on Zenodo and has a DOI, so it can be cited. It is also mirrored on GitHub and on Hugging Face, but the archived copy is the one to cite.

OPEN TEST FILESBrowse the corpus on Hugging Face

Inspect the dataset card and PDF files, or use the repository’s files and versions to reproduce a check. For a versioned citation, use the Zenodo DOI below.

What this is

If you are writing a converter, or choosing one, the hard part is not the happy path. It is knowing what happens when a document has two columns, or a heading that is only bold, or a scanned page in the middle. This corpus makes that testable in about a minute, and gives you somewhere to point when you find a bug.

Every PDF in it was written byte by byte from scratch — no PDF library, no reportlab — so the exact content stream behind any result can be read. When a converter does something surprising, it matters that the input is not a black box.

Why I built it

The failure modes documented in the guides on this site were not found by reading specifications. They were found by building documents that isolate one behaviour each, converting them, and reading the output. That process needed a fixed set of inputs — something you can re-run over time and compare against, rather than a folder of ad-hoc test files that nobody can reproduce.

Having built it, publishing it costs nothing and saves the next person from rebuilding the same set. The corpus is released into the public domain for that reason.

What is in it

18 PDFs, 18 measured results, and the two scripts that build and check the corpus:

FileWhat it isolates
01-single-column.pdfThe case everything is built for — a baseline to compare against
02-two-column-shared-baseline.pdfTwo columns on identical baselines
03-table.pdfTable cells sharing a baseline
04-no-text-layer.pdfA page with no text operators at all
05-two-column-offset.pdfTwo columns that overlap for only part of the page
06-three-column.pdfThree columns sharing baselines
07-bold-headings-body-size.pdfHeadings marked out only by weight, not size
08-running-header-2-pages.pdfThe same header and footer repeated on every page
09-hyphenated-breaks.pdfWords split across lines with a trailing hyphen
10-toc-wide-gap.pdfA wide horizontal gap inside one visual line
11-scan-styled.pdfBars standing in for a scanned page
12-mixed-worst-case.pdfTitle, two-column paragraph and table on one page
13-heading-thresholds.pdfOne line at each of six font sizes around the usual thresholds
14a-row-major.pdfIdentical geometry to 14b, text written row by row
14b-column-major.pdfIdentical geometry to 14a, text written column by column
15-mixed-first-page-text.pdfPage 1 has text, page 2 is a scan
16-mixed-middle-page-text.pdfScan, text, scan
17-compressed-stream.pdfA FlateDecode-compressed content stream

Two rules apply to all of them. Each file isolates one behaviour, so a surprising result points at a single cause. And where two files are compared they differ in one variable only — 14a and 14b are the clearest case: same page size, same font, same coordinates, the same four left-column and four right-column strings, the same nine text items in total. The only difference is the order those text operators appear in the content stream.

The scripts

The pair worth looking at first

14a-row-major.pdf and 14b-column-major.pdf are visually indistinguishable. Open them side by side and you cannot tell which is which.

RECORDED OUTPUT · 17 SEP 2026A single space changes what you readThe first body line from the corpus’s recorded extraction.
14a · Row-majorLeft column, line one.␠Right column, line one.

Columns joined with a space.

14b · Column-majorLeft column, line one.Right column, line one.

Columns joined with no separating space.

␠ marks the existing space for visibility. Both recorded outputs merge two columns; this is a failure illustration, not a corrected result. Inspect the files and results.json.

Yet the measured output differs. Both files begin with a heading naming which one they are, and then the same four lines of body text. On the row-major file, the first body line came out as:

Left column, line one. Right column, line one.

and on the column-major file:

Left column, line one.Right column, line one.

No space at all between the columns. Both are wrong, but the second is far more dangerous, because line one.Right column reads as a typo rather than as two columns that have been fused together. Nobody proofreading the output catches that.

The cause is not the geometry. It is that PDF.js emits the spacing between two text runs as its own synthetic text item when the runs are written consecutively, and omits it otherwise. Same page, same coordinates, different outcome — a property of the file's internal byte order that no amount of looking at the page will reveal. This is documented in more detail in the two-column guide.

How to use it

git clone https://github.com/GordonGuoJin/pdf-to-markdown-test-corpus
cd pdf-to-markdown-test-corpus
python3 verify.py

Then open your converter, drop in the files from tests/, and compare what you get against results.json.

Your converter's results will differ from the measured ones. That is the point — the value is in having a fixed set of inputs you can compare over time.

How to cite it

The archived copy has the DOI 10.5281/zenodo.22884428, which resolves at doi.org/10.5281/zenodo.22884428. The record itself is at zenodo.org/records/22884428.

The citation string, as Zenodo issues it:

BayKolm (2026). PDF-to-Markdown Test Corpus [Data set]. Zenodo. https://doi.org/10.5281/zenodo.22884428

If you are referring to the corpus as a whole rather than to this specific release, the concept DOI is 10.5281/zenodo.22884427 — that one always resolves to the most recent version. The record DOI above is pinned to the version published on 22 September 2026.

Licence

The PDFs and scripts are released under CC0 1.0 — public domain. Use them for anything, including commercially, with no attribution required. They are synthetic documents built for testing and contain no third-party content.

Related guides

Convert a PDF now