The command
npx truecopy a-document.pdf - the shortest way to find out whether this library is any use on your own files, before installing anything.
npx truecopy a-document.pdf
No install, no project, no line of code. Any file it can open — a PDF, a CSV, a TSV, a table pasted out of a viewer and saved to disk. One engine reads all of them; only the ruler changes, and the output says which one it used.
What it prints
truecopy - statement.pdf
pdf, 3 page(s), 128 row(s)
page 1 - cut at 68, 229, 356, 411, 490
7 column(s), 41 row(s)
col kind filled role
0 - 41% -
1 - 68% -
2 - 24% -
...
1 30/05/2026 | VIR SEPA SALAIRE | 30/05/2026 | 2400,00
2 28/05/2026 | CARTE BOULANGER | 28/05/2026 | 7,90
...
what this reading cannot vouch for:
- column 5 of page 1 is filled on only 17% of its rows - the cut may have invented it
- the pages disagree on how many columns there are (6 on 2, 7 on 1)
Three blocks, and the last one is the point.
The cut
cut at 68, 229, 356… is where the columns were placed, in PDF units from the left edge. It is the layer where a reading goes wrong first: get the cut wrong and every judgement after it inherits the mistake.
The line names its ruler when it is not a page’s. A list of numbers with no unit is a riddle:
page 1 - cut at 100, 290 a PDF
page 1 - cut at characters 7, 22 a table pasted with spaces
page 1 - cut on the delimiter a CSV, where the columns ARE the fields
page 1 - no boundary nothing lined up; every row came back whole
Points stay unlabelled — they are what a page has always been measured in. And on a delimited file the numbers themselves say nothing anybody needs, so it names what happened instead.
The columns
How often each column is actually filled. kind and role stay empty here on purpose — the command names no kinds and no thresholds, because the whole point is to be useful before you have told it anything about your documents. Fill those in and they appear; see explain.
What it cannot vouch for
The list at the bottom is computed without knowing anything about your document:
- a page with no column at all — either it is prose, or the cut failed and every row came back whole;
- a column filled on a fraction of its rows — the cut may have invented it out of a letterhead or an address block;
- pages cut differently from one another — usually a different table, and joining them makes a third that is neither;
- a page carrying no text at all — a blank page, a scan, or an image.
When the list is empty it says so, and says what that means:
nothing looked wrong from the shape of this page.
that is not the same as "this reading is right".
Exit codes
| Code | When |
|---|---|
0 |
the file was read, whatever the warnings say |
1 |
the file was refused, or no file was given |
A refusal is worded for a person and carries its reason in brackets — empty, too-big, no-text, too-slow, not-opened:
This PDF carries no text: it is most likely a scan or a photo. (no-text)
Why the command exists
Because the honest way to sell a reader is to let people run it on the document that worries them. If the cut comes out wrong on your statements, you find that out in thirty seconds instead of after an afternoon of integration — and the output tells you which layer failed, so you know whether the fix is a header anchor, a threshold, or a different library entirely.
What it deliberately does not do
It does not extract to CSV or JSON. That would make it a converter, and a converter that quietly gets a column wrong is exactly what this library exists to avoid. For rows in your own code, two lines do it — with the same warnings attached.