A PDF stores text at coordinates, not a table. Here is how the grid is recovered — exactly where the borders allow it, and by measurement where they do not.
A PDF contains fragments of text with positions, not rows and columns, so every extractor has to infer the grid back out. Where a table is ruled, the borders are vector paths and the grid is read straight from them — exact. Where it is not, columns come from the gaps between text bands, which handles left-aligned labels and right-aligned figures identically. Several tables on one page are detected and offered separately, a table running across pages can be joined into one sheet, and ✂ Choose table lets you drag a box round any area. Output is .xlsx, .csv, .json or the clipboard. Open the extractor.
Runs in your browser. No account, no upload, no page limit.
This is the root of every difficulty on this page, and it is not obvious from looking at one. A PDF stores fragments of text, each with a position, a font and a size. The rows and columns you see are an arrangement of those positions and nothing more. There is no row object, no column object, no cell. Any extractor has to infer the grid back out of the coordinates.
Which means there is no such thing as a lossless PDF table extraction, and anything claiming otherwise is describing a different file format. What there is: two methods, one of which is exact when it applies, and a set of specific failures worth knowing about.
A table drawn with borders already states its own geometry. The rules are vector paths in the page with exact coordinates, so where they exist they are read and used directly — no inference at all. The grid matches the printed one because it is the printed one.
This is read as vectors, straight from the page's drawing operators. Camelot, the best-known implementation of the same idea, renders the page to an image first and finds the lines in pixels; doing it from the operators means no rendering step and no resolution to lose. Borders drawn as very thin filled rectangles rather than strokes — which plenty of producers do — are picked up as well.
Three things it fixes that inference gets wrong: a heading above the table can no longer bridge its columns, because the heading is outside the rules; an empty cell stays an empty cell instead of shifting everything after it; and a cell whose text wraps onto a second line stays one cell.
A framed callout is not a table, so the rules have to enclose at least two rows and two columns before they are treated as one, and a single underline beneath a heading is ignored.
Most tables are not ruled, so the columns have to come from the gaps. The naive approach — group text by where each fragment starts horizontally — fails on the most common table in existence: one with right-aligned figures. A column of numbers aligned right starts at a different horizontal position on every row, so grouping by start position invents a fresh column for each one.
Instead every fragment is treated as occupying a horizontal band. Those bands are merged, and the gaps left between them are the column separators. Left-aligned labels, right-aligned totals and centred headings all resolve to the same columns, because the gaps between them are the same either way.
The title. A page headed Acme Bank — Statement of Account with Page 1 of 3 over on the right holds two pieces of text separated by a wide gap, which from the outside is indistinguishable from a table row. But the title string is long enough to cover the gap between the first two columns — and because the bands are merged, one line covering a gap welds two columns together for every row on the page.
Measured on a five-column statement: Date and Description came back in the same cell on every row of every page. That is the commonest document anyone brings to a tool like this, so the fix is worth being precise about.
Throwing the row away is the wrong answer. A subtotal line reading TOTAL … 4.65 also holds exactly two cells, and losing a real data row is far worse than keeping a heading. So the rows that end up in your spreadsheet and the rows that decide where the columns go are treated as two different sets. Rows are considered most table-like first, and a row is only given a say in the column positions if adding it does not merge columns the earlier rows already agreed on. A title line is kept out of that vote and still lands in the sheet, in the columns the vote produced.
Two simpler rules were tried first and both measured wrong, which is worth recording because both sound obviously right. Ignoring rows set in a larger font than the body wrecks any table whose header is bigger than its body — the header is frequently the only row establishing the last column, so that column vanishes. Ignoring rows with too few cells fixes a five-column statement and cannot help a three-column one, where a two-cell title is only one cell short of a real row.
Very common — an invoice with its line items above a totals block, a report with a summary beside a breakdown. Read as a single grid they interfere: the narrow table's columns force extra columns onto the wide one and both headings land in the wrong place. Measured on a page holding a three-column table above a five-column one, the whole-page read gives eleven rows of six columns with both headings misplaced; taken separately they give a clean 5 × 3 and a clean 5 × 5.
Blocks stacked down the page are separated by four signals together — a line with fewer than two cells, an unusually large vertical gap, a sustained change in how many columns the rows hold, and a run length requirement so that a single subtotal line cannot split a table in half.
Two tables printed next to each other are harder, and the reason is a genuinely interesting measurement. An ordinary invoice — labels on the left, figures pushed to the right margin — has an internal gap of 60.5 character-widths, three times wider than a real gutter between two tables at 19.4. Any width threshold that finds the gutter also cuts that invoice down the middle, and cutting a real table in half is far worse than not splitting one.
So the test is structural rather than metric: only the widest gap in a block is ever a candidate, both sides must hold at least two columns of their own, and the gap has to be substantially wider than the next widest. A two-up layout whose gutter is no wider than its own column gaps is not detected, and the tool says so rather than guessing — that case is genuinely ambiguous from position alone, and the answer is to draw a box round each side by hand.
Which you can always do. ✂ Choose table draws the page as you would see it in a reader with the tables it found outlined on top; tap one, or drag a box round any area at all.
A table continuing onto the next page is grouped back into one, so a statement running pages 1 to 3 with a summary on page 4 becomes two sheets rather than four or one. The grouping rule and the ten-document measurement behind it are covered on bank statement to Excel, where multi-page documents are the norm.
The claim at the top of this page — that a PDF stores positions rather than structure — has one exception worth knowing about, because it is the future of this problem rather than a curiosity.
PDFs can carry an accessibility structure tree: a parallel description of the document's meaning, marking headings as headings, lists as lists and tables as tables with genuine row and cell elements. It exists for screen readers, and it is what the PDF/UA standard and accessibility legislation in the EU and the United States increasingly require of published documents. In a properly tagged file the table is not inferred at all — it is declared.
Two reasons that has not solved anything yet. First, tagging is rare outside government publications and large organisations with an accessibility programme; the overwhelming majority of invoices, statements and reports in circulation carry no tags whatsoever. Second, when tags are present they are frequently wrong — produced automatically by an export filter, with cells grouped by visual proximity rather than meaning, which is the same inference again wearing a badge of authority.
We do not read the structure tree today, and the page says so rather than implying a capability. Reading it is a sound idea and the obvious next step for a document that has one; trusting it blindly over a measurement is not. If your documents are reliably tagged — a publisher's output, or a government form — a library that honours the tree will beat position-based inference on those files, and that is a fair reason to use one.
Stated plainly, because every one of these produces output that looks plausible rather than failing loudly — which is the dangerous kind of wrong.
The common thread is that a PDF records appearance, and everything above is meaning that was expressed visually and never written down. No extractor recovers it, and one that claims to is inferring — which is fine as long as it says so.
Table extraction from PDFs has a small, well-known set of tools, and they solve different problems. Worth knowing which one your situation calls for, because this is not always it.
| Tool | Shape | Where it fits |
|---|---|---|
| Camelot (Python) | Library, command line | The reference implementation of lattice and stream extraction. Excellent for a repeatable pipeline over a known document type, once you have Python, Ghostscript and some tuning time. Its lattice mode rasterises the page to find lines; ours reads them as vectors. |
| Tabula (Java) | Desktop app and library | A graphical page where you draw boxes round tables, then export. Mature, and the closest in spirit to our Choose table. Needs a Java runtime installed. |
| pdfplumber (Python) | Library | Lower level — gives you characters, lines and rectangles and lets you write the table logic yourself. The right choice when your documents are strange and you want control rather than a guess. |
| Acrobat export | Paid desktop | Very good on documents Adobe's own products produced, and it handles OCR. Subscription, and a desktop install. |
| Online converters | Web service | No install, but the document is uploaded to somebody else's machine and usually carries a page or file-size limit on the free tier. |
| This page | Browser | No install, no upload, no limit, and you can see the result page by page before committing. Not scriptable: there is no command line and no endpoint to call. |
The honest summary: if you are writing a pipeline that will process ten thousand documents unattended, use a library. If you have a document in front of you right now and it must not be uploaded, this is the one that works — and the browser is the only place those two constraints meet without an installation.
The same extracted grid, in whatever shape the next step needs: .xlsx with one sheet per table and real column widths, .csv, .json as an array of records keyed by the table's own column names, or straight onto the clipboard for Google Sheets as tab-separated text plus an HTML table so it lands in cells rather than one column.
Numbers that are unambiguously numbers become real numeric cells — thousands separators removed, an accounting negative in brackets turned into a negative, a percentage turned into the fraction a spreadsheet expects. Anything that could be misread is left alone: dates keep their formatting rather than becoming serial numbers, and a reference like 12A stays exactly as written, because guessing wrong on a reference number is worse than leaving it as text.
Everything runs in your browser. The PDF is never uploaded, which is the whole reason this exists rather than being another wrapper around a server.