Understanding PDF Structure: Why PDF to Markdown Conversion Is Challenging

A technical deep-dive into how PDFs store content internally, why text extraction and reflow are hard, and how modern converters tackle table detection, multi-column layouts, and OCR.

Ben
Ben
April 22, 2026
Understanding PDF Structure: Why PDF to Markdown Conversion Is Challenging

Why PDF to Markdown Is Harder Than It Looks

When I started building PDF2MD I assumed PDF parsing was a solved problem. There are libraries everywhere, the format is decades old, and Markdown is simple. Surely you just call a function and get clean text out.

After a year of working on this, I want to write down what I actually learned, because I keep getting questions about why my converter (or anyone's converter) handles some PDFs badly. The short answer is that PDFs aren't structured documents, even though they look like structured documents.

What's actually in a PDF

A PDF is a sequence of drawing instructions. Roughly: "draw the character 'H' at coordinates (72, 720) using font Helvetica at 12 points. Now draw 'e' at (78, 720). Now draw 'l' at (84, 720)…" That's it. There's no concept of "this is a paragraph" or "this is a heading" or "this is a row of a table." The PDF format does support some of those concepts via tagged PDF, but in practice almost nothing in the wild uses them.

So when you read a PDF, what you have is positioned characters. Everything else — paragraphs, headings, tables, reading order, columns — has to be reconstructed by inference.

This is the root of every PDF parsing problem.

How you reconstruct paragraphs

The basic approach is: group characters that are close together horizontally into words, group words on the same line into lines, and group lines that are close together vertically into paragraphs. Each step has thresholds, and the thresholds are different for every PDF depending on font size and line spacing.

Most of the time this works. The cases it breaks: very tight line spacing (paragraphs merge), very loose line spacing (single paragraph splits in two), drop caps (the first letter of a chapter is huge and gets parsed as its own paragraph), and PDFs where the text was rendered with one character per draw call (each character is a separate "word" because the spacing detection trips).

How you guess at headings

PDFs don't say "this is a heading." Converters guess based on font size, font weight, and whether the line is short and has whitespace above and below. A line in 18-point bold with empty space around it is probably a heading. A line in 12-point regular running into adjacent text is probably body.

The guessing breaks when:

  • The document uses a small font for everything and headings are only marked by bold.
  • The document uses one big font for everything (rare but it happens with poetry or legal documents).
  • "Body" text is sometimes bold for emphasis, and the converter promotes that emphasis to a heading.
  • Captions under figures are short and isolated and look like headings to the heuristic.

There's no way to fix this perfectly without ML, which is what Marker does — it has a model that classifies regions of the page. That moves the problem from "rules and thresholds" to "data and training", which is more accurate but also harder to debug when it's wrong.

Why tables are the worst part

In a Word doc or an HTML page, a table is a table. There's a table element with rows and cells. In a PDF, a table is a bunch of text strings positioned in a grid. The converter has to:

  1. Detect that this region is a table at all (rather than columns of unrelated text).
  2. Find the column boundaries (where do the vertical lines go, even if no lines are drawn).
  3. Find the row boundaries (where do horizontal breaks happen, even if no lines are drawn).
  4. Assign each text fragment to the right cell.
  5. Handle wrapped cells, where one cell's content runs onto two visual lines.
  6. Handle merged cells, where one cell spans two columns or two rows.

Every step can fail. Step 5 (wrapped cells) is the one that bites me most. If a cell's content wraps to a second line, a naive parser sees that second line as a new row. Suddenly your three-row table is a six-row table with every other row mostly empty.

The good converters use a few signals at once: ruling lines if they exist, alignment of text edges, consistency of column widths across rows. Even with all that, hard tables (financial reports, scientific data tables with merged headers) sometimes need a manual fix.

Multi-column layouts

A two-column page is just two streams of text that happen to be drawn on the same physical page. The PDF doesn't say "column A reads first, then column B." The reader knows because of visual convention.

Converters detect columns by looking at vertical bands of whitespace running down the page. If you find a clear vertical strip of empty space, it's probably a column gutter, and the text on the left should be read before the text on the right.

This breaks when:

  • Figures span both columns (now the gutter is interrupted).
  • Headings span both columns (same problem).
  • The columns are different widths (the gutter is harder to find).
  • There are footnotes at the bottom that break the column flow.

Academic papers are notorious for all four. Marker handles them well; my browser-only approach handles them okay; a basic text extractor handles them badly.

Reading order

Even within a column, reading order can be ambiguous. A pull quote is positioned in the middle of the page, breaking the surrounding text. A figure caption sits next to a figure, but should it be read before or after the body paragraph it relates to? A sidebar might read top to bottom or it might be supplementary.

Most converters punt on this and read top-to-bottom within each column. It's wrong sometimes, but it's a defensible default.

OCR is a different problem entirely

Everything above assumes the PDF has selectable text. If the PDF is scanned — a photograph of a page, or a document scanned from paper — there's no text to extract. The "characters" are just pixel patterns.

To handle these, you need OCR (optical character recognition), which is a whole separate technology. Tesseract is the open-source standard. Modern alternatives use vision-language models (the same family as GPT-4V), which are more accurate but heavier.

PDF2MD doesn't do OCR. Adding it would mean shipping a model into the browser that's at least 50MB, possibly more, and OCR is slow. I'd rather keep the page light and tell people to OCR first, separately. It's a tradeoff I keep revisiting.

What this means for you

If you're using a converter and the output is wrong, the failure mode usually maps to one of the above:

  • Empty output → scanned PDF, needs OCR.
  • Garbled order → multi-column not detected.
  • Wrong heading hierarchy → font-based heuristic was confused.
  • Tables broken → cells wrapped or rules absent.
  • Pages run together → page break heuristic confused by running headers.

Knowing the cause doesn't always fix the problem, but it tells you whether to try a different converter (Marker for layout, OCR for scans) or just retype the broken section.

What I'm working on

The areas I'm actively trying to improve in PDF2MD:

  • Better column detection on academic papers.
  • Smarter table cell merging when wrapping is detected.
  • Optional OCR via WASM, on a toggle, for the small cases where scanning a flyer is what someone actually wants.

If you have a PDF that breaks the converter in an interesting way, send it to [email protected]. The pathological cases are how I find the bugs.

Last updated: April 22, 2026

Understanding PDF Structure: Why PDF to Markdown Conversion Is Challenging | PDF to Markdown Converter | Fast, Secure, Online PDF to MD | pdf2md.net