Pandoc PDF to Markdown: Working Conversion Routes
Pandoc cannot convert PDF directly to Markdown. If you still have the original DOCX, HTML, or EPUB, convert that source with Pandoc. If only the PDF remains, use a PDF extractor or OCR-capable tool first. Pandoc is optional afterward, when you need a different Markdown dialect.
Choose the shortest route that matches your file:
| What you have | Start here | Check before using the result |
|---|---|---|
| Original DOCX, HTML, or EPUB | Convert the original with Pandoc | Headings, lists, tables, and image assets |
| Selectable PDF text | Try a PDF text extractor | Complete sentences and table rows |
| Columns or difficult table headers | Compare a layout-aware extraction | Reading order and which header belongs to each value |
| Image-only pages or a mixed PDF | Use an OCR-capable configuration or service | Text from every relevant page, plus tables and formulas |
Check more than the first page. A selectable cover does not mean a scanned appendix contains a usable text layer.
Why the direct Pandoc command fails
This command intentionally demonstrates the unsupported input:
pandoc input.pdf -t gfm -o output.md
The original Pandoc 3.11 test returned exit code 21 and:
Unknown input format 'pdf'
Pandoc can convert to PDF, but not from PDF.
The PDF-input probe records the same failure across all eight PDF fixtures. The recorded input-format list and Pandoc FAQ confirm the boundary. Installing a PDF output engine or changing -t does not add a PDF reader.
Route 1: Convert the original document with Pandoc
Use the command for the source you actually have:
# Original Word document
pandoc input.docx -f docx -t gfm --wrap=none -o output.md
# Original HTML document
pandoc input.html -f html -t gfm --wrap=none -o output.md
# Original EPUB
pandoc input.epub -f epub -t gfm --wrap=none -o output.md
These are alternatives, not steps in a PDF conversion chain. The original test executed each command successfully; its snippet-check record contains the exit codes.
The test Word report retained the measured heading levels, lists, prose, and table rows. Its raw Markdown output lets you inspect those structures. The HTML and EPUB controls also retained the structures measured for their fixtures.
If the DOCX contains pictures, add media extraction and package the assets with the Markdown. Follow the Pandoc DOCX image workflow for a tested relative-path layout.
A DOCX reconstructed from a PDF is a different input. This experiment did not test Word's PDF import or a PDF-to-DOCX service. If reconstruction already scrambles columns or replaces a table with positioned text, a later Pandoc command does not restore the original relationships automatically.
Route 2: Extract a selectable PDF locally
The following examples were checked against the original fixtures with the installed versions listed below. They assume the relevant package is already installed and input.pdf is in the working directory.
MarkItDown: start with simple selectable content
from pathlib import Path
from markitdown import MarkItDown
result = MarkItDown(enable_plugins=False).convert_local("input.pdf")
Path("output.md").write_text(result.markdown, encoding="utf-8")
This tested path preserved single-column prose and the simple invoice's rows. It also emitted pipe tables, so “MarkItDown never extracts PDF tables” would be inaccurate for this installation.
It struggled with the two-column fixture. The raw output alternates unrelated fragments before finishing a sentence:
Invoice reconciliation starts with the export file, not
Demand forecasts fail upward more often than they
the ledger. Teams that reconcile weekly pull the
If your output switches subjects mid-sentence, try a different extraction path before chunking or indexing it. Smaller chunks do not repair an already scrambled reading order. The MarkItDown PDF guide examines that path in more detail.
PyMuPDF4LLM: compare when reading order is wrong
This example uses the PyMuPDF4LLM 1.28.2 API from the original test. Check your installed version before adopting it.
from pathlib import Path
import pymupdf4llm
text = pymupdf4llm.to_markdown("input.pdf")
Path("output.md").write_text(text, encoding="utf-8")
The default call preserved the tested column sample's paragraph order. That result is a reason to compare it on similar input, not a guarantee for sidebars, footnotes, floating figures, or every multicolumn page.
Grouped and rotated headers still needed review. The grouped-header output began:
|**Item**|**Qty**|**Price**||
|---|---|---|---|
|||**Unit**|**Total**|
A renderer can display this table while leaving the parent-child header relationship implicit. If the next system needs records, check the source and use explicit columns such as “Unit price” and “Total price.” If presentation matters, retain a table representation that expresses the grouping.
For more local-code options and selection criteria, see converting PDF to Markdown in Python.
Route 3: Use OCR when pages contain pictures of text
In the original tests, neither the plugins-disabled MarkItDown path nor the default PyMuPDF4LLM call recovered the image-only page's words. Both also omitted the scanned portion of the mixed document.
That is a result for these configurations. It does not establish that either project lacks all OCR options. PyMuPDF4LLM documents OCR configuration; this test did not add or force an OCR engine.
To validate an OCR route:
- Pick a distinctive sentence visible on an image-only page.
- Search for it in the extracted Markdown.
- Repeat on scanned pages later in a mixed document.
- Check numbers, headings, table labels, and formulas against the page, not just whether the job succeeded.
If you prefer a service route, Markovo's PDF to Markdown converter recovered the scan passage in the retained test output. Markovo is the service we build. That result came from supplied outputs, not a new service request in this evaluation.
The same output still needed review. Tables used HTML, and the grouped table did not explicitly encode its column spans. The formula sample contained nested math delimiters and altered expressions, including this raw equation output:
$$
\[\mathsf{E}=\mathsf{m}\mathsf{c}\mathsf{^2}\]
$$
Recovering words from a scan does not establish correct mathematics or table semantics. Compare those structures separately before reuse. For a programmatic service workflow, the PDF-to-Markdown API guide covers the relevant review and delivery questions.
Do you need Pandoc after extraction?
Not necessarily. Both Python examples already write Markdown. If that output meets your destination's requirements, review it and stop there.
Use Pandoc afterward only for a deliberate format change. For example, when the extracted and reviewed input uses Pandoc Markdown syntax:
pandoc -f markdown -t gfm --wrap=none extracted.md -o output.md
Select the reader that matches the actual input, especially when it contains raw HTML tables, footnotes, or math. The Pandoc Markdown cheat sheet explains these choices.
This is an optional serialization step, not a tested repair for lost PDF structure. It cannot infer omitted scan text, restore a sentence from interleaved columns, or decide which price belongs under a missing header.
Review the result before handing it off
- Compare a complete paragraph from every distinct page layout with the PDF.
- Trace table values to their headers, including grouped or rotated labels.
- Search for text from scanned pages, not just selectable ones.
- Compare equations, superscripts, and operators individually.
- Open the output in the final consumer and check whether it accepts HTML tables and the emitted math syntax.
If the output is going into retrieval or an LLM pipeline, do these checks before chunking it. The PDF-to-Markdown LLM guide focuses on the consequences of broken order, tables, and missing pages.
Test scope, dates, and raw evidence
The original evaluation ran on October 16, 2026, using Python 3.11.14, Pandoc 3.11, MarkItDown 0.1.8, and PyMuPDF4LLM 1.28.2. MarkItDown used enable_plugins=False. PyMuPDF4LLM used the default to_markdown call without an added or forced OCR engine.
The controlled PDF fixtures covered single-column prose, two columns, a simple table, grouped headers, rotated labels, an image-only page, a mixed document, and selectable linear equations. They were small synthetic English examples, not a representative accuracy benchmark. Original DOCX, HTML, and EPUB files were separate source-format controls, not reconstructed PDF intermediates.
Key observed boundaries were:
| Question | Original result |
|---|---|
| Can Pandoc read these PDFs? | All eight input probes failed with the missing-PDF-reader error. |
| Did the selectable prose and simple table survive? | Both local PDF paths retained the measured prose and table rows. |
| Did the two-column sample stay in order? | MarkItDown interleaved fragments; PyMuPDF4LLM preserved the measured order. |
| Did the local defaults recover scanned text? | Neither recovered the scan text in these fixtures. |
| Did retained Markovo output recover the scan? | Yes, but its table representation and equation output still required review. |
The complete original scores remain available. Their sentence, anchor, and same-line cell checks are diagnostic measures, not proof of merged-header semantics, mathematical equivalence, or downstream retrieval quality. No tool-wide quality or speed ranking follows from them.
The snippet checks verified the original Python examples against every PDF fixture and matched their output to the runner. They also checked the source-format commands and the intentionally failing PDF command. The installed MarkItDown PDF converter source is retained for implementation context.
The supplied Markovo mixed-document snapshot and equation snapshot date to October 6, 2026. Their OCR counters disagree: the mixed job has pages_ocr=3 versus billing pagesOcr=1, and the equation job has pages_ocr=1 versus billing pagesOcr=0. Those records cannot establish an exact final page-routing history. Their rate-card identifier is not an engine version, and cached zero-second timings are not service-latency measurements.
The useful boundary is straightforward: use Pandoc on supported structured sources; use extraction or OCR to recover PDF content; then choose the Markdown representation your destination can actually consume.


