# Quickstart Source: https://truecopy.dev/docs/quickstart/ Two lines give you rows and cells, plus the one field that separates this from an extractor. Three more steps turn a reading into one that checks itself. ## Before you install anything ```sh npx truecopy a-statement.pdf ``` It prints the cut it made into columns, what share of rows each column fills, the rows themselves, and then, on its own, what the reading **could not vouch for**. It takes thirty seconds and needs no project and no code. If it makes nothing of your documents, you have lost nothing. ## Install ```sh npm install truecopy ``` It needs Node 20 or later, ships as ESM only and is MIT licensed. `pdfjs-dist` is an **optional** peer. Install it only if you read PDFs. ```sh npm install pdfjs-dist # PDFs only. A paste or a CSV needs nothing. ``` ## Rows and cells, in two lines ```ts import { readTable } from 'truecopy'; const { rows, warnings } = await readTable(file); ``` `rows` is `string[][]`: every row of every page, cut into cells. If that is all you want, destructure it and go. `file` is whatever was dropped: a PDF, a table pasted out of one, a CSV, a TSV. **One engine reads all of them.** The cut votes on the left edges that come back row after row, and that question does not care whether they were measured in points, in characters or in field indices. The `readTable` page shows [where each one starts](https://truecopy.dev/docs/table/), and what the reading prints when it could not cut at all. `warnings` is why this is not just another extractor: ```ts // [] // or // ['column 3 of page 2 is filled on only 8% of its rows - the cut may have invented it'] ``` > **An empty `warnings` is not a promise that the reading is right.** It means nothing looked wrong from the shape of the page, which is a much smaller claim. Every warning is computed without knowing anything about your document. ### In a browser pdf.js refuses to start in a browser without a worker, and truecopy does not resolve that URL for you. Every bundler spells it differently, and choosing one would lock you into it. ```ts // Vite import workerSrc from 'pdfjs-dist/legacy/build/pdf.worker.mjs?url'; const { rows } = await readTable(file, { workerSrc }); ``` In Node you can leave it out. pdf.js then runs inline, which is slower on a long document and correct everywhere. ## When the rows have to be trusted Two lines are enough while nothing acts on the result. The moment something does (a budget, a report, a decision), the question changes from _did I get rows_ to _are these the right rows_. Three steps get you there, cheapest first. ### 1. Say what a cell is The library has never heard of a date or an amount. You name the kind. The library counts, divides and compares. ```ts import { findRowAnomalies, thresholdsFor } from 'truecopy/signature'; const signature = { kindOf: (cell: string) => isDate(cell) ? 'date' : isAmount(cell) ? 'amount' : 'text', thresholds: { ...thresholdsFor(['amount', 'text'], 0.6), date: { share: 0.6, emptyIsAnomalyAbove: 0.7 } } }; const anomalies = findRowAnomalies(rows, signature); ``` Now the total line, the balance line and the footer come back marked, **without any list of forbidden words**. A table describes itself, and a row that breaks what every other row does is not a row of the table. ### 2. Set the reading against the document Most documents announce something about themselves: a statement carries `opening + Σ = closing`, a career record announces its total. That is the only check that depends on no layout at all. ```ts selfCheck(document, reading) { if (reading.header.declaredTotal === null) { return { nothing: 'this document announces no total' }; } return { declared: [reading.header.declaredTotal], read: reading.records.reduce((sum, row) => sum + row.amount, 0), unit: 'EUR' }; } ``` Saying `{ nothing }` costs a sentence, on purpose. An oversight becomes something written down instead of a silent `null`. ### 3. Drive it through the contract ```ts import { readDocument } from 'truecopy/contract'; const result = readDocument(document, myReader); // result.verdict: 'read' | 'needs-review' | 'refused' ``` The one rule the pipeline enforces: **a reading that contradicts its document never comes back as `read`.** You choose between refusing and asking for review. You cannot choose to say nothing. ## Where to go next - [The tutorial](https://truecopy.dev/docs/tutorial/) builds a complete reader, step by step, from an empty file. - [Why refusing matters](https://truecopy.dev/docs/why-refuse/) is the doctrine, and where it came from. - [The command](https://truecopy.dev/docs/cli/) is the thirty-second version, in detail. - [The demo](https://truecopy.dev/demo/) runs all of this in your browser on a file you pick. Map of this site for a model: https://truecopy.dev/llms.txt Every page in one file: https://truecopy.dev/llms-full.txt