visura-parser 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +113 -0
- package/dist/errors.d.ts +6 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +9 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/extract-pdf.d.ts +3 -0
- package/dist/internal/extract-pdf.d.ts.map +1 -0
- package/dist/internal/extract-pdf.js +114 -0
- package/dist/internal/extract-pdf.js.map +1 -0
- package/dist/internal/join-pages.d.ts +7 -0
- package/dist/internal/join-pages.d.ts.map +1 -0
- package/dist/internal/join-pages.js +37 -0
- package/dist/internal/join-pages.js.map +1 -0
- package/dist/internal/map-document.d.ts +4 -0
- package/dist/internal/map-document.d.ts.map +1 -0
- package/dist/internal/map-document.js +113 -0
- package/dist/internal/map-document.js.map +1 -0
- package/dist/internal/model.d.ts +20 -0
- package/dist/internal/model.d.ts.map +1 -0
- package/dist/internal/model.js +2 -0
- package/dist/internal/model.js.map +1 -0
- package/dist/internal/parse-capital.d.ts +5 -0
- package/dist/internal/parse-capital.d.ts.map +1 -0
- package/dist/internal/parse-capital.js +76 -0
- package/dist/internal/parse-capital.js.map +1 -0
- package/dist/internal/parse-classifications.d.ts +5 -0
- package/dist/internal/parse-classifications.d.ts.map +1 -0
- package/dist/internal/parse-classifications.js +40 -0
- package/dist/internal/parse-classifications.js.map +1 -0
- package/dist/internal/parse-document.d.ts +5 -0
- package/dist/internal/parse-document.d.ts.map +1 -0
- package/dist/internal/parse-document.js +385 -0
- package/dist/internal/parse-document.js.map +1 -0
- package/dist/internal/parse-people.d.ts +9 -0
- package/dist/internal/parse-people.d.ts.map +1 -0
- package/dist/internal/parse-people.js +231 -0
- package/dist/internal/parse-people.js.map +1 -0
- package/dist/internal/text.d.ts +12 -0
- package/dist/internal/text.d.ts.map +1 -0
- package/dist/internal/text.js +99 -0
- package/dist/internal/text.js.map +1 -0
- package/dist/internal/values.d.ts +5 -0
- package/dist/internal/values.d.ts.map +1 -0
- package/dist/internal/values.js +64 -0
- package/dist/internal/values.js.map +1 -0
- package/dist/parse-visura.d.ts +3 -0
- package/dist/parse-visura.d.ts.map +1 -0
- package/dist/parse-visura.js +17 -0
- package/dist/parse-visura.js.map +1 -0
- package/dist/types.d.ts +180 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +73 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Visura Parser contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# visura-parser
|
|
2
|
+
|
|
3
|
+
Visura Parser is a TypeScript library for deterministic parsing of Italian
|
|
4
|
+
_Visure camerali_. It accepts text-based PDF bytes and returns typed data in
|
|
5
|
+
the [documented output schema](outputSchema.json).
|
|
6
|
+
|
|
7
|
+
The project is inspired by INDA's
|
|
8
|
+
[Visura Parser](https://api.inda.ai/desk/docs/Visura%20Parser/): the PDF must
|
|
9
|
+
be text based.
|
|
10
|
+
|
|
11
|
+
## Motivation
|
|
12
|
+
|
|
13
|
+
When working with Italian companies, you may need to obtain structured data about them. And yet ther isn't really a free and reliable solution to do that.
|
|
14
|
+
This is what happened at Silkware, where we work with and onboard Italian companies on a daily basis.
|
|
15
|
+
The _Visura camerale_ is an official document that contains a satisfying amount of information about a company that is easy to obtain and to share for companies.
|
|
16
|
+
Currently, there isn't a free and open source solution to extract this information in a structured way.
|
|
17
|
+
|
|
18
|
+
## Disclaimer
|
|
19
|
+
|
|
20
|
+
This the development of this library is strongly aided by AI coding agents such as GPT5.6.
|
|
21
|
+
|
|
22
|
+
## Requirements
|
|
23
|
+
|
|
24
|
+
- Node.js 20 or newer for consumers
|
|
25
|
+
- [Bun](https://bun.sh/) 1.3.12 for contributors
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { readFile } from 'node:fs/promises';
|
|
31
|
+
import { parseVisura, VisuraParseError } from 'visura-parser';
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const bytes = new Uint8Array(await readFile('visura.pdf'));
|
|
35
|
+
const data = await parseVisura(bytes, { filename: 'visura.pdf' });
|
|
36
|
+
console.log(data.companyName, data.reaNumber);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
if (error instanceof VisuraParseError) {
|
|
39
|
+
console.error(error.code);
|
|
40
|
+
} else {
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The parser performs no filesystem or network access. `filename` is optional
|
|
47
|
+
and is copied to `filename`; it is not inferred from the PDF.
|
|
48
|
+
|
|
49
|
+
Missing fields are omitted. Counts and monetary amounts are numbers; explicit
|
|
50
|
+
availability values are booleans. Dates use ISO `YYYY-MM-DD`. Official Italian
|
|
51
|
+
role names are preserved in structured officer and shareholder records.
|
|
52
|
+
See the [example output](exampleOutput.json) and
|
|
53
|
+
[the schema](outputSchema.md).
|
|
54
|
+
|
|
55
|
+
Ordinary, historical, evasion reports, and the supported shareholder Visura
|
|
56
|
+
block are recognized. A block has no `reportType`. Image-only PDFs,
|
|
57
|
+
password-required inputs, invalid PDFs, and unrelated documents produce typed
|
|
58
|
+
errors; there is no OCR or language-model fallback.
|
|
59
|
+
|
|
60
|
+
## Development
|
|
61
|
+
|
|
62
|
+
Parse a local PDF and print its JSON without building first:
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
bun run parse -- "/path/to/visura.pdf"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The command writes JSON to stdout and errors to stderr. It exits with code 1
|
|
69
|
+
on a read or parse failure, or 2 for invalid arguments. Use `--help` for usage.
|
|
70
|
+
The output can contain personal data; keep it local and out of Git.
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
bun install
|
|
74
|
+
bun run check
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Useful commands:
|
|
78
|
+
|
|
79
|
+
| Command | Purpose |
|
|
80
|
+
| ----------------------- | ------------------------------------------- |
|
|
81
|
+
| `bun run build` | Compile the package into `dist/` |
|
|
82
|
+
| `bun run typecheck` | Check TypeScript without emitting files |
|
|
83
|
+
| `bun run test` | Run the test suite once |
|
|
84
|
+
| `bun run test:watch` | Run tests while files change |
|
|
85
|
+
| `bun run lint` | Run ESLint |
|
|
86
|
+
| `bun run format` | Format supported files |
|
|
87
|
+
| `bun run package:check` | Inspect the built npm package and its types |
|
|
88
|
+
|
|
89
|
+
To verify the supplied private corpus without logging document values:
|
|
90
|
+
|
|
91
|
+
```sh
|
|
92
|
+
bun run corpus:check -- /path/to/visure-examples --reference
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The reference gate accounts for all 346 inputs: 341 supported text-based
|
|
96
|
+
documents, three textless PDFs, one invalid PDF, and one unrelated PDF. It
|
|
97
|
+
validates the complete schema, checks core identity presence, and compares
|
|
98
|
+
repeated parses. Omit `--reference` for another folder.
|
|
99
|
+
|
|
100
|
+
Each run saves successful parser outputs in a fresh `corpus-output/run-…/`
|
|
101
|
+
directory, ignored by Git. A source named `example.pdf` produces
|
|
102
|
+
`example.pdf.json`. The JSON summary includes the absolute `outputDirectory`
|
|
103
|
+
path. Rejected inputs have no output file; their counts remain in the summary.
|
|
104
|
+
These files can contain personal data, just like the source PDFs.
|
|
105
|
+
|
|
106
|
+
## Contributing
|
|
107
|
+
|
|
108
|
+
Read [`CONTRIBUTING.md`](CONTRIBUTING.md) before adding fixtures. Real Visure
|
|
109
|
+
often contain personal data and must not be committed.
|
|
110
|
+
|
|
111
|
+
## License
|
|
112
|
+
|
|
113
|
+
[MIT](LICENSE)
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type VisuraParseErrorCode = 'invalid-pdf' | 'password-required' | 'unsupported-pdf' | 'text-unavailable' | 'resource-limit' | 'malformed-content';
|
|
2
|
+
export declare class VisuraParseError extends Error {
|
|
3
|
+
readonly code: VisuraParseErrorCode;
|
|
4
|
+
constructor(code: VisuraParseErrorCode, message: string, options?: ErrorOptions);
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,oBAAoB,GAC5B,aAAa,GACb,mBAAmB,GACnB,iBAAiB,GACjB,kBAAkB,GAClB,gBAAgB,GAChB,mBAAmB,CAAC;AAExB,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;gBAGlC,IAAI,EAAE,oBAAoB,EAC1B,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,YAAY;CAMzB"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAQA,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IAChC,IAAI,CAAuB;IAEpC,YACE,IAA0B,EAC1B,OAAe,EACf,OAAsB;QAEtB,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;QAC/B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Public package entry point. */
|
|
2
|
+
export { VisuraParseError } from './errors.js';
|
|
3
|
+
export { parseVisura } from './parse-visura.js';
|
|
4
|
+
export type { VisuraParseErrorCode } from './errors.js';
|
|
5
|
+
export type { Representative, Activity, AtecoClassification, ShareCapital, Employees, CompanySummary, BusinessRegisterFilings, Certifications, AvailableDocuments, Officer, Shareholder, VisuraDocument, ParseVisuraOptions, } from './types.js';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAClC,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACxD,YAAY,EACV,cAAc,EACd,QAAQ,EACR,mBAAmB,EACnB,YAAY,EACZ,SAAS,EACT,cAAc,EACd,uBAAuB,EACvB,cAAc,EACd,kBAAkB,EAClB,OAAO,EACP,WAAW,EACX,cAAc,EACd,kBAAkB,GACnB,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAClC,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract-pdf.d.ts","sourceRoot":"","sources":["../../src/internal/extract-pdf.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAiB,YAAY,EAAkB,MAAM,YAAY,CAAC;AA0D9E,wBAAsB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC,CAmGzE"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { getDocument, InvalidPDFException, } from 'pdfjs-dist/legacy/build/pdf.mjs';
|
|
2
|
+
import { VisuraParseError } from '../errors.js';
|
|
3
|
+
const PDF_SIGNATURE = '%PDF-';
|
|
4
|
+
const MAX_INPUT_BYTES = 50 * 1024 * 1024;
|
|
5
|
+
const MAX_PAGES = 200;
|
|
6
|
+
const MAX_SPANS = 250_000;
|
|
7
|
+
const MAX_CHARACTERS = 10_000_000;
|
|
8
|
+
function hasPdfSignature(bytes) {
|
|
9
|
+
if (bytes.byteLength < PDF_SIGNATURE.length)
|
|
10
|
+
return false;
|
|
11
|
+
for (let index = 0; index < PDF_SIGNATURE.length; index += 1) {
|
|
12
|
+
if (bytes[index] !== PDF_SIGNATURE.charCodeAt(index))
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
function isTextItem(item) {
|
|
18
|
+
return (typeof item === 'object' &&
|
|
19
|
+
item !== null &&
|
|
20
|
+
'str' in item &&
|
|
21
|
+
typeof item.str === 'string' &&
|
|
22
|
+
'transform' in item &&
|
|
23
|
+
Array.isArray(item.transform));
|
|
24
|
+
}
|
|
25
|
+
function toSpan(item, page) {
|
|
26
|
+
const text = item.str.trim();
|
|
27
|
+
if (text.length === 0)
|
|
28
|
+
return undefined;
|
|
29
|
+
const x = Number(item.transform[4]);
|
|
30
|
+
const y = Number(item.transform[5]);
|
|
31
|
+
if (!Number.isFinite(x) || !Number.isFinite(y))
|
|
32
|
+
return undefined;
|
|
33
|
+
return {
|
|
34
|
+
page,
|
|
35
|
+
text,
|
|
36
|
+
x,
|
|
37
|
+
y,
|
|
38
|
+
width: item.width,
|
|
39
|
+
height: item.height,
|
|
40
|
+
fontName: item.fontName,
|
|
41
|
+
hasEol: item.hasEOL,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export async function extractPdf(bytes) {
|
|
45
|
+
if (bytes.byteLength > MAX_INPUT_BYTES) {
|
|
46
|
+
throw new VisuraParseError('resource-limit', 'The PDF exceeds the supported input size.');
|
|
47
|
+
}
|
|
48
|
+
if (!hasPdfSignature(bytes)) {
|
|
49
|
+
throw new VisuraParseError('invalid-pdf', 'Input does not have a PDF signature.');
|
|
50
|
+
}
|
|
51
|
+
const loadingTask = getDocument({
|
|
52
|
+
data: bytes.slice(),
|
|
53
|
+
isEvalSupported: false,
|
|
54
|
+
useWorkerFetch: false,
|
|
55
|
+
verbosity: 0,
|
|
56
|
+
});
|
|
57
|
+
try {
|
|
58
|
+
const document = await loadingTask.promise;
|
|
59
|
+
if (document.numPages > MAX_PAGES) {
|
|
60
|
+
throw new VisuraParseError('resource-limit', 'The PDF exceeds the supported page count.');
|
|
61
|
+
}
|
|
62
|
+
const pages = [];
|
|
63
|
+
let spanCount = 0;
|
|
64
|
+
let characterCount = 0;
|
|
65
|
+
for (let pageNumber = 1; pageNumber <= document.numPages; pageNumber += 1) {
|
|
66
|
+
const page = await document.getPage(pageNumber);
|
|
67
|
+
const viewport = page.getViewport({ scale: 1 });
|
|
68
|
+
const content = await page.getTextContent({
|
|
69
|
+
includeMarkedContent: false,
|
|
70
|
+
});
|
|
71
|
+
const spans = [];
|
|
72
|
+
for (const item of content.items) {
|
|
73
|
+
if (!isTextItem(item))
|
|
74
|
+
continue;
|
|
75
|
+
const span = toSpan(item, pageNumber);
|
|
76
|
+
if (span !== undefined) {
|
|
77
|
+
spans.push(span);
|
|
78
|
+
spanCount += 1;
|
|
79
|
+
characterCount += span.text.length;
|
|
80
|
+
}
|
|
81
|
+
if (spanCount > MAX_SPANS || characterCount > MAX_CHARACTERS) {
|
|
82
|
+
throw new VisuraParseError('resource-limit', 'The PDF exceeds the supported text size.');
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
pages.push({
|
|
86
|
+
number: pageNumber,
|
|
87
|
+
width: viewport.width,
|
|
88
|
+
height: viewport.height,
|
|
89
|
+
spans,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
if (!pages.some((page) => page.spans.length > 0)) {
|
|
93
|
+
throw new VisuraParseError('text-unavailable', 'The PDF contains no extractable text on any page.');
|
|
94
|
+
}
|
|
95
|
+
return { pages };
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
if (error instanceof VisuraParseError)
|
|
99
|
+
throw error;
|
|
100
|
+
if (error instanceof Error && error.name === 'PasswordException') {
|
|
101
|
+
throw new VisuraParseError('password-required', 'The PDF requires a password and cannot be parsed.', { cause: error });
|
|
102
|
+
}
|
|
103
|
+
if (error instanceof InvalidPDFException) {
|
|
104
|
+
throw new VisuraParseError('invalid-pdf', 'The PDF is invalid or truncated.', {
|
|
105
|
+
cause: error,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
throw new VisuraParseError('malformed-content', 'The PDF could not be interpreted safely.', { cause: error });
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
await loadingTask.destroy();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=extract-pdf.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract-pdf.js","sourceRoot":"","sources":["../../src/internal/extract-pdf.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ExtractedPage } from './model.js';
|
|
2
|
+
/** A continuous reading area for records that continue on the next page.
|
|
3
|
+
* Source page numbers remain attached to spans. Repeated margin chrome is
|
|
4
|
+
* excluded on detail pages; the original extraction is never modified.
|
|
5
|
+
*/
|
|
6
|
+
export declare function joinPages(pages: readonly ExtractedPage[]): ExtractedPage;
|
|
7
|
+
//# sourceMappingURL=join-pages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"join-pages.d.ts","sourceRoot":"","sources":["../../src/internal/join-pages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAkB,MAAM,YAAY,CAAC;AAEhE;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,GAAG,aAAa,CAoCxE"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/** A continuous reading area for records that continue on the next page.
|
|
2
|
+
* Source page numbers remain attached to spans. Repeated margin chrome is
|
|
3
|
+
* excluded on detail pages; the original extraction is never modified.
|
|
4
|
+
*/
|
|
5
|
+
export function joinPages(pages) {
|
|
6
|
+
const first = pages[0];
|
|
7
|
+
const width = first?.width ?? 595;
|
|
8
|
+
const spans = [];
|
|
9
|
+
let bottom;
|
|
10
|
+
for (const page of pages) {
|
|
11
|
+
const content = page.spans.filter((span) => page.number === 1 ||
|
|
12
|
+
(span.y > page.height * 0.06 && span.y < page.height * 0.91));
|
|
13
|
+
if (content.length === 0)
|
|
14
|
+
continue;
|
|
15
|
+
const top = content.reduce((highest, span) => Math.max(highest, span.y), -Infinity);
|
|
16
|
+
const offset = bottom === undefined ? 0 : bottom - 13 - top;
|
|
17
|
+
for (const span of content) {
|
|
18
|
+
spans.push({
|
|
19
|
+
...span,
|
|
20
|
+
page: page.number,
|
|
21
|
+
x: (span.x * width) / page.width,
|
|
22
|
+
width: (span.width * width) / page.width,
|
|
23
|
+
y: span.y + offset,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
bottom =
|
|
27
|
+
content.reduce((lowest, span) => Math.min(lowest, span.y), Infinity) +
|
|
28
|
+
offset;
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
number: first?.number ?? 1,
|
|
32
|
+
width,
|
|
33
|
+
height: first?.height ?? 842,
|
|
34
|
+
spans,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=join-pages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"join-pages.js","sourceRoot":"","sources":["../../src/internal/join-pages.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,KAA+B;IACvD,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACvB,MAAM,KAAK,GAAG,KAAK,EAAE,KAAK,IAAI,GAAG,CAAC;IAClC,MAAM,KAAK,GAAqB,EAAE,CAAC;IACnC,IAAI,MAA0B,CAAC;IAC/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAC/B,CAAC,IAAI,EAAE,EAAE,CACP,IAAI,CAAC,MAAM,KAAK,CAAC;YACjB,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,IAAI,IAAI,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,CAC/D,CAAC;QACF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QACnC,MAAM,GAAG,GAAG,OAAO,CAAC,MAAM,CACxB,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,EAC5C,CAAC,QAAQ,CACV,CAAC;QACF,MAAM,MAAM,GAAG,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,EAAE,GAAG,GAAG,CAAC;QAC5D,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;YAC3B,KAAK,CAAC,IAAI,CAAC;gBACT,GAAG,IAAI;gBACP,IAAI,EAAE,IAAI,CAAC,MAAM;gBACjB,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,IAAI,CAAC,KAAK;gBAChC,KAAK,EAAE,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,GAAG,IAAI,CAAC,KAAK;gBACxC,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,MAAM;aACnB,CAAC,CAAC;QACL,CAAC;QACD,MAAM;YACJ,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC;gBACpE,MAAM,CAAC;IACX,CAAC;IACD,OAAO;QACL,MAAM,EAAE,KAAK,EAAE,MAAM,IAAI,CAAC;QAC1B,KAAK;QACL,MAAM,EAAE,KAAK,EAAE,MAAM,IAAI,GAAG;QAC5B,KAAK;KACN,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { VisuraDocument } from '../types.js';
|
|
2
|
+
/** Maps recognized scalar facts into the published schema without inventing defaults. */
|
|
3
|
+
export declare function mapDocument(fields: Readonly<Partial<Record<string, string>>>): VisuraDocument;
|
|
4
|
+
//# sourceMappingURL=map-document.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"map-document.d.ts","sourceRoot":"","sources":["../../src/internal/map-document.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlD,yFAAyF;AACzF,wBAAgB,WAAW,CACzB,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,GAChD,cAAc,CA8GhB"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { parseItalianBoolean, parseItalianCount } from './values.js';
|
|
2
|
+
/** Maps recognized scalar facts into the published schema without inventing defaults. */
|
|
3
|
+
export function mapDocument(fields) {
|
|
4
|
+
const result = {};
|
|
5
|
+
for (const key of [
|
|
6
|
+
'registeredOfficeAddress',
|
|
7
|
+
'certifiedEmail',
|
|
8
|
+
'reaNumber',
|
|
9
|
+
'taxCode',
|
|
10
|
+
'vatNumber',
|
|
11
|
+
'leiCode',
|
|
12
|
+
'legalForm',
|
|
13
|
+
'incorporationDate',
|
|
14
|
+
'registrationDate',
|
|
15
|
+
'lastProtocolDate',
|
|
16
|
+
]) {
|
|
17
|
+
const value = fields[key];
|
|
18
|
+
if (value !== undefined)
|
|
19
|
+
result[key] = value;
|
|
20
|
+
}
|
|
21
|
+
const activity = {};
|
|
22
|
+
for (const key of [
|
|
23
|
+
'status',
|
|
24
|
+
'startDate',
|
|
25
|
+
'primaryActivity',
|
|
26
|
+
'atecoCode',
|
|
27
|
+
'naceCode',
|
|
28
|
+
'importExportActivity',
|
|
29
|
+
'networkContract',
|
|
30
|
+
]) {
|
|
31
|
+
const value = fields[key];
|
|
32
|
+
if (value !== undefined)
|
|
33
|
+
activity[key] = value;
|
|
34
|
+
}
|
|
35
|
+
for (const key of [
|
|
36
|
+
'licensesAndRegistrations',
|
|
37
|
+
'environmentalRegistrations',
|
|
38
|
+
]) {
|
|
39
|
+
const value = fields[key];
|
|
40
|
+
if (value !== undefined)
|
|
41
|
+
activity[key] = [value];
|
|
42
|
+
}
|
|
43
|
+
if (Object.keys(activity).length)
|
|
44
|
+
result.activity = activity;
|
|
45
|
+
const summary = {};
|
|
46
|
+
for (const key of [
|
|
47
|
+
'shareholdersCount',
|
|
48
|
+
'directorsCount',
|
|
49
|
+
'officeHoldersCount',
|
|
50
|
+
'localUnitsCount',
|
|
51
|
+
'filingsLast12Months',
|
|
52
|
+
'shareTransfersCount',
|
|
53
|
+
'registeredOfficeTransfersCount',
|
|
54
|
+
]) {
|
|
55
|
+
const value = fields[key];
|
|
56
|
+
const count = value === undefined ? undefined : parseItalianCount(value);
|
|
57
|
+
if (count !== undefined)
|
|
58
|
+
summary[key] = count;
|
|
59
|
+
}
|
|
60
|
+
const equity = fields['hasEquityInterests'];
|
|
61
|
+
const hasEquity = equity === undefined ? undefined : parseItalianBoolean(equity);
|
|
62
|
+
if (hasEquity !== undefined)
|
|
63
|
+
summary.hasEquityInterests = hasEquity;
|
|
64
|
+
if (Object.keys(summary).length)
|
|
65
|
+
result.companySummary = summary;
|
|
66
|
+
const employees = fields['employeeCount'];
|
|
67
|
+
const employeeCount = employees === undefined ? undefined : parseItalianCount(employees);
|
|
68
|
+
if (employeeCount !== undefined)
|
|
69
|
+
result.employees = { count: employeeCount };
|
|
70
|
+
const filings = fields['filingCount'];
|
|
71
|
+
const filingCount = filings === undefined ? undefined : parseItalianCount(filings);
|
|
72
|
+
if (filingCount !== undefined)
|
|
73
|
+
result.businessRegisterFilings = { count: filingCount };
|
|
74
|
+
const certifications = {};
|
|
75
|
+
for (const key of ['soa', 'quality']) {
|
|
76
|
+
const value = fields[key];
|
|
77
|
+
if (value !== undefined)
|
|
78
|
+
certifications[key] = [value];
|
|
79
|
+
}
|
|
80
|
+
if (Object.keys(certifications).length)
|
|
81
|
+
result.certifications = certifications;
|
|
82
|
+
const documents = {};
|
|
83
|
+
for (const key of [
|
|
84
|
+
'companyFileAvailable',
|
|
85
|
+
'articlesOfAssociationAvailable',
|
|
86
|
+
]) {
|
|
87
|
+
const value = fields[key];
|
|
88
|
+
const available = value === undefined ? undefined : parseItalianBoolean(value);
|
|
89
|
+
if (available !== undefined)
|
|
90
|
+
documents[key] = available;
|
|
91
|
+
}
|
|
92
|
+
const acts = fields['otherActsCount'];
|
|
93
|
+
const count = acts === undefined ? undefined : parseItalianCount(acts);
|
|
94
|
+
if (count !== undefined)
|
|
95
|
+
documents.otherActsCount = count;
|
|
96
|
+
const years = fields['financialStatementYears'];
|
|
97
|
+
if (years !== undefined) {
|
|
98
|
+
// Only explicit years are facts. A trailing ellipsis does not imply intermediate years.
|
|
99
|
+
const tokens = years
|
|
100
|
+
.replace(/\.{3}$|…$/u, '')
|
|
101
|
+
.trim()
|
|
102
|
+
.split(/[\s,;]+/);
|
|
103
|
+
if (tokens.every((token) => /^\d{4}$/.test(token) &&
|
|
104
|
+
Number(token) >= 1900 &&
|
|
105
|
+
Number(token) <= 2200)) {
|
|
106
|
+
documents.financialStatementYears = [...new Set(tokens.map(Number))];
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (Object.keys(documents).length)
|
|
110
|
+
result.availableDocuments = documents;
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=map-document.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"map-document.js","sourceRoot":"","sources":["../../src/internal/map-document.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface PositionedSpan {
|
|
2
|
+
readonly page: number;
|
|
3
|
+
readonly text: string;
|
|
4
|
+
readonly x: number;
|
|
5
|
+
readonly y: number;
|
|
6
|
+
readonly width: number;
|
|
7
|
+
readonly height: number;
|
|
8
|
+
readonly fontName: string;
|
|
9
|
+
readonly hasEol: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface ExtractedPage {
|
|
12
|
+
readonly number: number;
|
|
13
|
+
readonly width: number;
|
|
14
|
+
readonly height: number;
|
|
15
|
+
readonly spans: readonly PositionedSpan[];
|
|
16
|
+
}
|
|
17
|
+
export interface ExtractedPdf {
|
|
18
|
+
readonly pages: readonly ExtractedPage[];
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/internal/model.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,SAAS,cAAc,EAAE,CAAC;CAC3C;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;CAC1C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/internal/model.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ShareCapital } from '../types.js';
|
|
2
|
+
import type { ExtractedPdf } from './model.js';
|
|
3
|
+
/** Reads only the current-state euro capital table, never cover or quota amounts. */
|
|
4
|
+
export declare function parseCapital(pdf: ExtractedPdf): ShareCapital | undefined;
|
|
5
|
+
//# sourceMappingURL=parse-capital.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-capital.d.ts","sourceRoot":"","sources":["../../src/internal/parse-capital.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAiB,YAAY,EAAkB,MAAM,YAAY,CAAC;AA0E9E,qFAAqF;AACrF,wBAAgB,YAAY,CAAC,GAAG,EAAE,YAAY,GAAG,YAAY,GAAG,SAAS,CAWxE"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { foldText, normalizeText, semanticSpans, spansInReadingOrder, } from './text.js';
|
|
2
|
+
import { parseItalianAmount } from './values.js';
|
|
3
|
+
const CAPITAL_KEYS = {
|
|
4
|
+
deliberato: 'authorized',
|
|
5
|
+
sottoscritto: 'subscribed',
|
|
6
|
+
versato: 'paidUp',
|
|
7
|
+
};
|
|
8
|
+
const CAPITAL_HEADING = /^capitale sociale in euro$/i;
|
|
9
|
+
const HEADING_LINE = /^(?:capitale sociale(?: in(?: euro)?)?|in euro|euro)(?:\s+|$)/i;
|
|
10
|
+
const CAPITAL_ROW = /^(deliberato|sottoscritto|versato)\s*:?\s*((?:\d{1,3}(?:\.\d{3})+|\d+)(?:,\d+)?|-|n\.d\.)?$/i;
|
|
11
|
+
function readTable(page, heading) {
|
|
12
|
+
const rows = [];
|
|
13
|
+
for (const span of spansInReadingOrder(page)) {
|
|
14
|
+
if (span.y > heading.y + 2)
|
|
15
|
+
continue;
|
|
16
|
+
const row = rows.at(-1);
|
|
17
|
+
if (row !== undefined && Math.abs((row[0]?.y ?? span.y) - span.y) <= 2) {
|
|
18
|
+
row.push(span);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
rows.push([span]);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const result = { currency: 'EUR' };
|
|
25
|
+
const seen = new Set();
|
|
26
|
+
let previousY = heading.y;
|
|
27
|
+
for (const row of rows) {
|
|
28
|
+
const first = row[0];
|
|
29
|
+
if (first === undefined)
|
|
30
|
+
continue;
|
|
31
|
+
// The observed table has consecutive rows. A gap or unrelated row ends it;
|
|
32
|
+
// later shareholder payments must never supply a missing company amount.
|
|
33
|
+
if (previousY - first.y > Math.max(heading.height, first.height) * 3)
|
|
34
|
+
break;
|
|
35
|
+
previousY = first.y;
|
|
36
|
+
let text = normalizeText(row
|
|
37
|
+
.sort((left, right) => left.x - right.x)
|
|
38
|
+
.map((span) => span.text)
|
|
39
|
+
.join(' '));
|
|
40
|
+
if (heading.y - first.y <= heading.height * 2) {
|
|
41
|
+
text = text.replace(HEADING_LINE, '').trim();
|
|
42
|
+
}
|
|
43
|
+
if (text.length === 0)
|
|
44
|
+
continue;
|
|
45
|
+
const match = CAPITAL_ROW.exec(text);
|
|
46
|
+
const label = match?.[1];
|
|
47
|
+
if (label === undefined)
|
|
48
|
+
break;
|
|
49
|
+
const key = CAPITAL_KEYS[foldText(label)];
|
|
50
|
+
if (key === undefined || seen.has(key))
|
|
51
|
+
break;
|
|
52
|
+
seen.add(key);
|
|
53
|
+
const amount = match?.[2];
|
|
54
|
+
if (amount !== undefined) {
|
|
55
|
+
const parsedAmount = parseItalianAmount(amount);
|
|
56
|
+
if (parsedAmount !== undefined)
|
|
57
|
+
result[key] = parsedAmount;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return seen.size === 0 ? undefined : result;
|
|
61
|
+
}
|
|
62
|
+
/** Reads only the current-state euro capital table, never cover or quota amounts. */
|
|
63
|
+
export function parseCapital(pdf) {
|
|
64
|
+
for (const page of pdf.pages) {
|
|
65
|
+
const headings = semanticSpans(page)
|
|
66
|
+
.filter((span) => CAPITAL_HEADING.test(normalizeText(span.text)))
|
|
67
|
+
.sort((left, right) => right.y - left.y || left.x - right.x);
|
|
68
|
+
for (const heading of headings) {
|
|
69
|
+
const capital = readTable(page, heading);
|
|
70
|
+
if (capital !== undefined)
|
|
71
|
+
return capital;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=parse-capital.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-capital.js","sourceRoot":"","sources":["../../src/internal/parse-capital.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,QAAQ,EACR,aAAa,EACb,aAAa,EACb,mBAAmB,GACpB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAIjD,MAAM,YAAY,GAA+C;IAC/D,UAAU,EAAE,YAAY;IACxB,YAAY,EAAE,YAAY;IAC1B,OAAO,EAAE,QAAQ;CAClB,CAAC;AAEF,MAAM,eAAe,GAAG,6BAA6B,CAAC;AACtD,MAAM,YAAY,GAChB,gEAAgE,CAAC;AACnE,MAAM,WAAW,GACf,8FAA8F,CAAC;AAEjG,SAAS,SAAS,CAChB,IAAmB,EACnB,OAAuB;IAEvB,MAAM,IAAI,GAAuB,EAAE,CAAC;IACpC,KAAK,MAAM,IAAI,IAAI,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC;QAC7C,IAAI,IAAI,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,CAAC;YAAE,SAAS;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QACxB,IAAI,GAAG,KAAK,SAAS,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YACvE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjB,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAiB,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IACjD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAoB,CAAC;IACzC,IAAI,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC;IAC1B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;QACrB,IAAI,KAAK,KAAK,SAAS;YAAE,SAAS;QAClC,2EAA2E;QAC3E,yEAAyE;QACzE,IAAI,SAAS,GAAG,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC;YAAE,MAAM;QAC5E,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC;QACpB,IAAI,IAAI,GAAG,aAAa,CACtB,GAAG;aACA,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC;aACvC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;aACxB,IAAI,CAAC,GAAG,CAAC,CACb,CAAC;QACF,IAAI,OAAO,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9C,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAC/C,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QAEhC,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrC,MAAM,KAAK,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;QACzB,IAAI,KAAK,KAAK,SAAS;YAAE,MAAM;QAC/B,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;QAC1C,IAAI,GAAG,KAAK,SAAS,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,MAAM;QAC9C,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACd,MAAM,MAAM,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;QAC1B,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,YAAY,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;YAChD,IAAI,YAAY,KAAK,SAAS;gBAAE,MAAM,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC;QAC7D,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC;AAC9C,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,YAAY,CAAC,GAAiB;IAC5C,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;QAC7B,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,CAAC;aACjC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;aAChE,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAC/D,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YACzC,IAAI,OAAO,KAAK,SAAS;gBAAE,OAAO,OAAO,CAAC;QAC5C,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { AtecoClassification } from '../types.js';
|
|
2
|
+
import type { ExtractedPdf } from './model.js';
|
|
3
|
+
/** Codes are read only inside an explicitly labelled ATECO/ATECORI block. */
|
|
4
|
+
export declare function parseClassifications(pdf: ExtractedPdf): AtecoClassification[];
|
|
5
|
+
//# sourceMappingURL=parse-classifications.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-classifications.d.ts","sourceRoot":"","sources":["../../src/internal/parse-classifications.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C,6EAA6E;AAC7E,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,YAAY,GAAG,mBAAmB,EAAE,CAkD7E"}
|