Visura Parser is a TypeScript library for deterministic parsing of Italian Visure camerali. It accepts text-based PDF bytes and returns typed data in the documented output schema.
The project is inspired by INDA's Visura Parser: the PDF must be text based.
When working with Italian companies, you often need structured data about them. We wanted an open source parser that we could inspect, test, and improve.
This came up at Silkware, where we work with and onboard Italian companies. The Visura camerale is easy for companies to obtain and share, and it contains enough official registry data to make structured extraction useful.
This library is developed with substantial help from AI coding agents. Contributions must meet the same fixture, correctness, and review requirements regardless of how the code was written. The parser should be treated as a best-effort tool, so it may fail for Visure layouts that are not in the private corpus. Feel free to open an issue if you encounter a layout that is not supported, or contribute yourself.
- Node.js 20 or newer for consumers
- Bun 1.3.12 for contributors
Install the ESM package in your application:
npm install visura-parserimport { readFile } from 'node:fs/promises';
import { parseVisura, VisuraParseError } from 'visura-parser';
try {
const bytes = new Uint8Array(await readFile('visura.pdf'));
const data = await parseVisura(bytes, { filename: 'visura.pdf' });
console.log(data.companyName, data.reaNumber);
} catch (error) {
if (error instanceof VisuraParseError) {
console.error(error.code);
} else {
throw error;
}
}The parser performs no filesystem or network access. filename is optional
and is copied to filename; it is not inferred from the PDF.
Missing fields are omitted. Counts and monetary amounts are numbers; explicit
availability values are booleans. Dates use ISO YYYY-MM-DD. Official Italian
role names are preserved in structured officer and shareholder records. When
the source prints them, individual officers and shareholders also include birth
date, birthplace, birth-province code, citizenship, and residence address.
Current secondary offices and local business units are returned in localUnits,
in their printed order. When present, a local unit may also include its type,
trade name, secondary activity, local ATECO code, ATECO classifications, activity
declarations, and licences or authorisations. These filing and authorisation
values preserve the printed source prose. Classification versions are included
only when the source prints an explicit version heading. The local atecoCode
is selected from the first PRIMARIA or PREVALENTE classification in source
order, or the first unqualified classification; it is omitted for secondary-only
classifications.
See the local-unit corpus findings for field coverage
and layout constraints.
See the example output and
the schema.
Ordinary, historical, evasion reports, and the supported shareholder Visura
block are recognized. A block has no reportType. Image-only PDFs,
password-required inputs, invalid PDFs, and unrelated documents produce typed
errors; there is no OCR or language-model fallback.
Parse a local PDF and print its JSON without building first:
bun run parse -- "/path/to/visura.pdf"The command writes JSON to stdout and errors to stderr. It exits with code 1
on a read or parse failure, or 2 for invalid arguments. Use --help for usage.
The output can contain personal data; keep it local and out of Git.
bun install
bun run checkUseful commands:
| Command | Purpose |
|---|---|
bun run build |
Compile the package into dist/ |
bun run typecheck |
Check TypeScript without emitting files |
bun run test |
Run the test suite once |
bun run test:watch |
Run tests while files change |
bun run lint |
Run ESLint |
bun run format |
Format supported files |
bun run package:check |
Inspect the built npm package and its types |
To verify the supplied private corpus without logging document values:
bun run corpus:check -- /path/to/visure-examples --referenceThe reference gate accounts for all 346 inputs: 341 supported text-based
documents, three textless PDFs, one invalid PDF, and one unrelated PDF. It
validates the complete schema, checks core identity presence, and compares
repeated parses. Omit --reference for another folder.
Each run saves successful parser outputs in a fresh corpus-output/run-…/
directory, ignored by Git. A source named example.pdf produces
example.pdf.json. The JSON summary includes the absolute outputDirectory
path. Rejected inputs have no output file; their counts remain in the summary.
These files can contain personal data, just like the source PDFs.
Read the contributor guide before adding fixtures. Real Visure often contain personal data and must not be committed.
The public tests and synthetic fixtures work without access to the private corpus. To measure the built package on Node.js:
bun run benchmark
bun run benchmark -- /path/to/local/pdfs 3The benchmark prints aggregate timings and result hashes, without filenames or document values. See the performance review and audit for the measurement method, findings, and remaining work.