Extract Tables from a PDF

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.

Quick answer:

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.

Does a PDF contain a table?
No — it stores fragments of text with positions, fonts and sizes. The rows and columns you see are an arrangement of those positions, so every extractor has to infer the grid back out.
When is the extraction exact?
When the table is ruled. Borders are vector paths with real coordinates, so the grid is read straight from them rather than inferred — and a heading above the table can no longer bridge its columns.
Can it handle two tables on one page?
Yes, stacked or side by side. A page with a three-column table above a five-column one reads whole as eleven rows of six columns; taken separately it gives a clean 5×3 and 5×5.
What can it not recover?
Rotated headings, merges that left no visible trace, nested tables, colour used as meaning, charts and form fields. All of those are meaning expressed visually and never written down.
Can I use it in a script?
No — this is a browser page, not an API. For an unattended pipeline use a library: Camelot, Tabula or pdfplumber. This is the route for a document in front of you that must not be uploaded.
🧩 Open the PDF table extractor

Runs in your browser. No account, no upload, no page limit.

A PDF does not contain a table

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.

Method one: read the rules, when the table has them

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.

Method two: read the whitespace, and why not the left edges

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 line that breaks this, and it is on almost every document

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.

More than one table on a page

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.

Side by side, where width alone cannot decide

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.

Tables that run across pages

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 exception: a tagged PDF really does contain a table

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.

The tables this cannot recover, and why

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.

How this compares with the other extractors

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.

ToolShapeWhere it fits
Camelot (Python)Library, command lineThe 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 libraryA 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)LibraryLower 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 exportPaid desktopVery good on documents Adobe's own products produced, and it handles OCR. Subscription, and a desktop install.
Online convertersWeb serviceNo 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 pageBrowserNo 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.

What comes out

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.

Where to go next

Frequently Asked Questions

A tool that recovers rows and columns from a PDF, because a PDF does not contain a table. It stores fragments of text with positions, fonts and sizes; the rows and columns you see are an arrangement of those positions and nothing more. The extractor infers the grid back out — from the table's ruling lines where it has them, and from the gaps between text where it does not.
Yes, and the ruled case is exact. Borders in a PDF are vector paths with real coordinates, so where they exist the grid is read straight from them rather than inferred. That fixes three things inference gets wrong: a heading above the table cannot bridge its columns, an empty cell stays empty rather than shifting everything after it, and a wrapped cell stays one cell.
Because of the page title. A heading with a page number on the right holds two pieces of text separated by a wide gap, which looks exactly like a table row — and the title string is long enough to cover the gap between the first two columns, welding them together for every row. Here the rows that go into the spreadsheet and the rows that decide the column positions are two different sets, so a title still reaches your sheet without getting a vote on the columns.
Yes. Measured on a page with a three-column table above a five-column one, reading the whole page gives eleven rows of six columns with both headings misplaced; taken separately they give a clean 5 by 3 and a clean 5 by 5. Stacked tables and side-by-side ones are both detected and offered as choices, and Choose table lets you tap one or drag a box round any area instead.
Because width alone cannot decide it. An ordinary invoice with labels left and figures pushed right has an internal gap of 60.5 character-widths — three times wider than a real gutter between two tables at 19.4. Every threshold that finds the gutter also cuts that invoice in half, which is worse. A two-up layout whose gutter is no wider than its own column gaps is not detected, and the page says so rather than guessing; draw a box round each side instead.
Yes — choose Merge matching tables, which is suggested when the pages really do line up. A page continues the one before it when the column count matches, the headings match or are absent, and every column still sits where it did. Over ten multi-page fixtures that got 10 of 10 right with nothing wrongly merged and nothing wrongly split. Repeated headings on later pages are removed and the count is reported.
Excel .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. Numbers that are unambiguously numbers become real numeric cells; dates and reference codes are deliberately left as text.
No. The whole extraction runs in your browser — reading the PDF, finding the columns, building the spreadsheet. That is the reason this exists rather than being another wrapper around a server, and you can confirm it in the Network tab.
Free, with no page limit, no file size cap, no account and no watermark. Those limits exist elsewhere to cap somebody's server costs; the extraction runs on your machine, so there is nothing to meter. The practical limit is your device's memory on a very large document, which is most likely to bite on a phone.
Yes, two ways. Enter a page range such as 1-3, 7 before extracting and only those pages are read, which is much faster on a long document. Or extract everything and step through the preview, since the download options let you keep one sheet per page. To take part of a page, use Choose table and drag a box round the area.
Not in one press. There is no batch queue because there is no server to queue on — each document is opened and converted by your own browser. For a handful of files that is a minute's work; for thousands, a library and a script is the right tool, and the comparison above says which.
Indirectly, and usefully. Set Format to JSON and you get an array of records keyed by the table's own column names with real numbers rather than strings, which pandas reads directly. CSV works too. What there is not is an endpoint to call — this is a browser page, not an API, so it fits a one-off or a sensitive document rather than an automated pipeline.
The column geometry does, because it is measured from positions rather than read. A digitally generated Arabic, Hebrew, Chinese or Japanese PDF extracts its columns correctly. What is English-only is the recognition for scanned pages and the document labelling, so a scan in another script will not be read at all.
Usually because a column is empty on that page. A column with nothing in it anywhere on the page leaves no text to occupy its band, so that page comes back one column narrower and everything after it moves left. It is also why such a page is treated as a separate table when pages are grouped — merging it anyway would shift every cell across, which is worse than an extra sheet.

Related Tools