Free PDF to Markdown converter
PDF to Markdown, text layer and all
Drop in a PDF and this free page reads its text layer without an account and without an upload: it decompresses the file, runs the text-showing operators, decodes each font back to Unicode, and rebuilds headings, lists and paragraphs from type size and position. Files up to 25 MB are handled, and a 228-page manual converts in about seven tenths of a second. Scanned PDFs contain no text layer at all — those are reported as scans rather than returned empty.
- 100% free
- No signup
- Up to 25 MB
- Detects scanned pages
- No OCR, no guessing
Drop a PDF here
Or choose one from your disk, or press Ctrl+V with a PDF on the clipboard. Up to 25 MB, and the file never leaves this tab.
How to convert a PDF to Markdown
Drop the file, read what the extractor found, keep the Markdown.
Hand over the PDF
Drop the file on the target, press Choose a PDF, or copy a file in your file manager and press Ctrl+V (⌘V) here. Files up to 25 MB are read; a 228-page 1.2 MB manual takes about 0.7 seconds and a one-page invoice about 10 milliseconds, all of it inside this tab.
Read the report above the output
Before the Markdown you get the facts: page count, PDF version, the producer that wrote the file, and how long the text layer took to read. If any page holds an image and no text it is named there, because that page is a scan and nothing can be extracted from it without optical character recognition, which this converter does not do.
Adjust the reconstruction, then take the file
Headings are inferred from type size, so switch them off for a document that has none; lists, hyphen rejoining and running-head removal each have their own tick box; the Pages field accepts 1-4, 9 to convert part of a long document. Copy Markdown or Save .md writes the result under the PDF's own name.
Technical specifications
| What is read | The text layer only — the Tj, TJ, ' and " operators inside each page's content stream, plus text drawn through form XObjects nested up to 8 deep |
|---|---|
| Decompression | FlateDecode through the browser's DecompressionStream, plus LZWDecode, ASCII85Decode, ASCIIHexDecode and RunLengthDecode, with PNG and TIFF predictors undone |
| File structure | Objects are found by scanning rather than by trusting the cross-reference table, so incremental updates and damaged xrefs still read; object streams are expanded |
| Character decoding | /ToUnicode CMaps including bfchar and bfrange, WinAnsi and MacRoman encodings, /Differences glyph names, and two-byte codes for composite fonts |
| Structure rebuilt | Headings ranked by type size, bullet and numbered lists, paragraphs joined by leading, monospaced runs fenced as code, hyphenated words rejoined, running heads and page numbers dropped |
| Cannot be done | No OCR of scanned pages, no encrypted files, no table reconstruction, no column-aware reading order, and no images, annotations or form-field values |
| Measured speed | A 228-page 1.2 MB manual yields 687,552 characters in about 0.7 s; a one-page 57 KB invoice takes about 10 ms; a 118-page scan is identified as text-free in about 0.2 s |
| Maximum file size | 25 MB, and up to 5,000 pages |
| Processing location | Your browser — the PDF is never uploaded |
| Price | Free, no signup, no page or document quota |
Frequently asked questions
Why is my PDF empty?
Almost certainly because it is a scan: the pages are photographs or fax images of text, and the characters exist only as pixels, with no text layer for anything to read. This page detects that case and says so rather than handing back an empty box — it reports which pages hold an image and no text, and a document where every page is like that gets told plainly that it needs OCR. You can check the same thing in any PDF viewer: try to select a line of text with the mouse, and if nothing highlights there is no text layer. The other, rarer cause is a file whose content streams use a compression filter this reader does not implement, which is named in the warnings instead.
Why is the text jumbled on a two-column page?
Because lines are grouped by their vertical position, and on a two-column layout the first line of the left column and the first line of the right column share one. A PDF stores no reading order — it stores glyphs at coordinates — so a converter either guesses at column boundaries or reads across the page, and reading across is the failure that is obvious rather than the one that silently drops half a sentence. For a two-column paper, converting one column's page range at a time does not help either; the practical answer is to fix the result by hand, or to get the document from its source format.
Why did my tables come out as lines of text?
Because a table in a PDF is usually not a table — it is text positioned in columns, sometimes with lines drawn around it as separate graphics that have no connection to the words. Rebuilding cell boundaries from x coordinates works on a clean grid and fails badly on merged cells, wrapped values and right-aligned numbers, and a wrong table is worse than an honest paragraph, so this converter does not attempt it. Rows come through as lines with their cells separated by spaces, which is usually enough to paste into the Markdown table generator and fix in a grid.
Can it open a password-protected PDF?
No. A file with an /Encrypt dictionary has its streams scrambled with a key derived from a password, and this page refuses it with a clear message rather than half-decoding it. That includes the common case of a PDF with an empty owner password and no user password, which technically anyone can open — the encryption is still real and the key computation is deliberately not implemented here. Remove the protection in the application that created the file, then convert the result.
Why are some characters missing or wrong?
Because a PDF stores glyph codes, not characters, and the map back to Unicode is optional. When a font ships a /ToUnicode map — most do — every code resolves exactly. When it does not, this reader falls back to the font's encoding, handling WinAnsi, MacRoman and a /Differences array of glyph names, which covers ordinary Latin text. What is left over is genuinely unrecoverable: a subset font with no map and non-standard glyph names, and symbol fonts such as Wingdings, where the code for a tick mark is also the code for a letter. Any font in that state is named in the report above the output, so a gap is explained rather than mysterious.
How does it decide what is a heading?
By type size, ranked against the body of the document. The most common size across all pages, weighted by how much text is set in it, is taken as body text; every distinct size noticeably larger than that is collected, sorted from largest down, and mapped to # then ## then ### in order. A short, bold, isolated line at body size is treated as one level deeper, which is how most reports mark subsections. It is a heuristic and it can be wrong in both directions — a pull quote set large becomes a heading, and a document typeset at one size throughout gets none — which is why the tick box turns the whole inference off.
Is my PDF uploaded anywhere?
No, and there is nowhere for it to go: this site is static files on a CDN with no server that accepts an upload. The file is read with the FileReader API, decompressed with the browser's own DecompressionStream, and parsed by JavaScript in your tab — a contract you can verify by opening the network panel while converting, or by switching off your connection first and watching it still work.
About the text inside a PDF
A PDF does not contain a document in any sense a text tool would recognise. It contains a program. Each page is a content stream of drawing operators, and the ones that matter here place a string of glyph codes at a coordinate in a font at a size: BT starts a text object, Tf selects the font, Tm sets the matrix that positions it, and Tj or TJ shows the codes. Nothing in that stream says “heading”, “paragraph” or even “line”. Word boundaries are frequently absent too — many producers never emit a space character, and instead nudge the matrix along by a negative number between two glyph runs, so the reader has to decide from the size of the nudge whether a space was intended. Reading a PDF back into text is therefore reconstruction from evidence, not extraction of something that was stored.
The evidence comes in several layers, and each one has to be undone. The file’s objects are usually compressed with Flate, sometimes bundled dozens at a time inside an object stream, and referenced through a cross-reference table that incremental edits leave stale — so this reader scans the whole file for object headers instead, letting the last definition of each number win. Glyph codes then have to become characters, which is what a font’s /ToUnicode map is for; without one, the encoding and any /Differences array are the only route, and for a subset font with invented glyph names there is no route at all. Only after all of that is there text to lay out, at which point headings are inferred by ranking type sizes, lists by their markers, and paragraphs by whether the vertical gap between two lines matches the leading.
Which makes the limits worth stating plainly rather than hiding. A scanned page has no text layer, so nothing here can read it and this page says so instead of returning silence. Encrypted files are refused. Tables come back as lines of text, because inventing cell boundaries from coordinates is the kind of guess that produces a confidently wrong table. Two-column layouts read across the page. When the result needs tidying — and on anything longer than a few pages it will — the Markdown editor is the place to do it with the rendering beside the source, and a row that should have been a table pastes neatly into the Markdown table generator. If the PDF was generated from HTML in the first place, converting the original page in the HTML to Markdown converter will always give a better result than reading the print-out of it.
Your file stays on your machine
A PDF is often the most sensitive thing on a desk — a contract, an invoice, a medical letter — so it matters that this one is parsed by JavaScript in your own tab and never sent anywhere. The site is static files with no upload endpoint behind them, and once the page has loaded, conversion keeps working with the network switched off.