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.
Files changed (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +113 -0
  3. package/dist/errors.d.ts +6 -0
  4. package/dist/errors.d.ts.map +1 -0
  5. package/dist/errors.js +9 -0
  6. package/dist/errors.js.map +1 -0
  7. package/dist/index.d.ts +6 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +4 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/internal/extract-pdf.d.ts +3 -0
  12. package/dist/internal/extract-pdf.d.ts.map +1 -0
  13. package/dist/internal/extract-pdf.js +114 -0
  14. package/dist/internal/extract-pdf.js.map +1 -0
  15. package/dist/internal/join-pages.d.ts +7 -0
  16. package/dist/internal/join-pages.d.ts.map +1 -0
  17. package/dist/internal/join-pages.js +37 -0
  18. package/dist/internal/join-pages.js.map +1 -0
  19. package/dist/internal/map-document.d.ts +4 -0
  20. package/dist/internal/map-document.d.ts.map +1 -0
  21. package/dist/internal/map-document.js +113 -0
  22. package/dist/internal/map-document.js.map +1 -0
  23. package/dist/internal/model.d.ts +20 -0
  24. package/dist/internal/model.d.ts.map +1 -0
  25. package/dist/internal/model.js +2 -0
  26. package/dist/internal/model.js.map +1 -0
  27. package/dist/internal/parse-capital.d.ts +5 -0
  28. package/dist/internal/parse-capital.d.ts.map +1 -0
  29. package/dist/internal/parse-capital.js +76 -0
  30. package/dist/internal/parse-capital.js.map +1 -0
  31. package/dist/internal/parse-classifications.d.ts +5 -0
  32. package/dist/internal/parse-classifications.d.ts.map +1 -0
  33. package/dist/internal/parse-classifications.js +40 -0
  34. package/dist/internal/parse-classifications.js.map +1 -0
  35. package/dist/internal/parse-document.d.ts +5 -0
  36. package/dist/internal/parse-document.d.ts.map +1 -0
  37. package/dist/internal/parse-document.js +385 -0
  38. package/dist/internal/parse-document.js.map +1 -0
  39. package/dist/internal/parse-people.d.ts +9 -0
  40. package/dist/internal/parse-people.d.ts.map +1 -0
  41. package/dist/internal/parse-people.js +231 -0
  42. package/dist/internal/parse-people.js.map +1 -0
  43. package/dist/internal/text.d.ts +12 -0
  44. package/dist/internal/text.d.ts.map +1 -0
  45. package/dist/internal/text.js +99 -0
  46. package/dist/internal/text.js.map +1 -0
  47. package/dist/internal/values.d.ts +5 -0
  48. package/dist/internal/values.d.ts.map +1 -0
  49. package/dist/internal/values.js +64 -0
  50. package/dist/internal/values.js.map +1 -0
  51. package/dist/parse-visura.d.ts +3 -0
  52. package/dist/parse-visura.d.ts.map +1 -0
  53. package/dist/parse-visura.js +17 -0
  54. package/dist/parse-visura.js.map +1 -0
  55. package/dist/types.d.ts +180 -0
  56. package/dist/types.d.ts.map +1 -0
  57. package/dist/types.js +2 -0
  58. package/dist/types.js.map +1 -0
  59. package/package.json +73 -0
@@ -0,0 +1,231 @@
1
+ import { foldText, normalizeText, semanticSpans } from './text.js';
2
+ import { parseItalianAmount } from './values.js';
3
+ import { joinPages } from './join-pages.js';
4
+ const OFFICER_ROLE = /^(?:presidente(?: (?:del )?consiglio (?:di |d')?amministrazione| del collegio sindacale)?|vice ?presidente(?: (?:del )?consiglio (?:di |d')?amministrazione)?|amministratore(?: unico| delegato)?|consigliere(?: delegato)?|sindaco(?: effettivo| supplente)?|titolare(?: dell'impresa individuale| di impresa individuale| firmatari[oa])?|liquidatore|procuratore(?: speciale| generale)?)$/i;
5
+ const SHAREHOLDER_ROLE = /^(?:soci[oa](?: unic[oa]| accomandante| accomandatari[oa])?|(?:nuda )?propriet[aà]'?|usufrutto)$/i;
6
+ const RIGHT = /^(?:(?:nuda )?propriet[aà]'?|usufrutto)$/i;
7
+ const TAX_CODE = /^(?:codice fiscale\s*:?\s*)?([A-Z0-9]{16}|\d{11})$/i;
8
+ const SECTION = /^(?:\d+\s+)(?:soci|amministratori|sindaci|titolari|altre cariche|attivit[aà]'?|sede|capitale|storia|informazioni|trasferimenti|scioglimento|protocollo)\b/i;
9
+ const PEOPLE_SECTION = /^\d+\s+(?:soci|amministratori|sindaci|titolari|altre cariche)\b/i;
10
+ const OWNERSHIP_ROW_TOLERANCE = 5;
11
+ export function isPersonRole(text) {
12
+ const value = normalizeText(text);
13
+ return OFFICER_ROLE.test(value) || SHAREHOLDER_ROLE.test(value);
14
+ }
15
+ function isName(text) {
16
+ return (text.length >= 2 &&
17
+ text.length <= 160 &&
18
+ /[a-zà-ù]/i.test(text) &&
19
+ !/[:;]|^\d|^[a-z]{0,3}\d{5,}/i.test(text) &&
20
+ !TAX_CODE.test(text) &&
21
+ !/^(?:elenco|numero|codice|nato|nata|residente|domicilio|residenza|indirizzo|\(?in carica|dal\s+\d|durata|quota|valore|tipo diritto|rappresentante|informazioni|amministratori|soci\b|sindaci|titolari|data |carica\b|poteri\b|registro|camera|visura|via\b|viale\b|piazza\b|cittadinanza|proprieta|versato|euro\b|capitale)/i.test(text) &&
22
+ !OFFICER_ROLE.test(text) &&
23
+ !SHAREHOLDER_ROLE.test(text));
24
+ }
25
+ function identities(page) {
26
+ const result = [];
27
+ const spans = semanticSpans(page);
28
+ for (const span of spans) {
29
+ const match = TAX_CODE.exec(normalizeText(span.text));
30
+ if (!match?.[1])
31
+ continue;
32
+ const name = page.spans
33
+ .filter((candidate) => candidate.y >= span.y - 2 &&
34
+ candidate.y - span.y < 85 &&
35
+ Math.abs(candidate.x - span.x) < 220 &&
36
+ isName(normalizeText(candidate.text)) &&
37
+ !page.spans.some((label) => Math.abs(label.y - candidate.y) <= 2 &&
38
+ label.x < candidate.x &&
39
+ /^(?:nato|nata|domicilio|residenza|indirizzo|cittadinanza)\b/i.test(normalizeText(label.text))))
40
+ .sort((a, b) => a.y - b.y)[0];
41
+ if (name &&
42
+ !result.some((item) => item.name === name && item.taxCode === match[1]))
43
+ result.push({ name, taxCode: match[1], y: span.y });
44
+ }
45
+ return result;
46
+ }
47
+ function adjacentName(page, role, roleFragments) {
48
+ const samePanel = (span) => span.x < page.width / 2 === role.x < page.width / 2;
49
+ const candidates = page.spans.filter((span) => span !== role &&
50
+ !roleFragments.has(span) &&
51
+ isName(normalizeText(span.text)));
52
+ return (candidates
53
+ .filter((span) => samePanel(span) &&
54
+ span.x > role.x + role.width - 2 &&
55
+ Math.abs(span.y - role.y) <= 4)
56
+ .sort((a, b) => a.x - b.x)[0] ??
57
+ candidates
58
+ .filter((span) => samePanel(span) &&
59
+ Math.abs(span.x - role.x) <= 35 &&
60
+ role.y - span.y >= 4 &&
61
+ role.y - span.y <= 34)
62
+ .sort((a, b) => b.y - a.y)[0]);
63
+ }
64
+ function fullName(page, first) {
65
+ const parts = [first.text];
66
+ let previous = first;
67
+ for (const span of [...page.spans].sort((a, b) => b.y - a.y)) {
68
+ if (span.y >= previous.y - 2 || Math.abs(span.x - first.x) > 3)
69
+ continue;
70
+ if (previous.y - span.y > 15 ||
71
+ Math.abs(span.height - first.height) > 0.5 ||
72
+ !isName(normalizeText(span.text)) ||
73
+ page.spans.some((label) => label.x < first.x - 10 && Math.abs(label.y - span.y) <= 2))
74
+ break;
75
+ parts.push(span.text);
76
+ previous = span;
77
+ }
78
+ return normalizeText(parts.join(' '));
79
+ }
80
+ function mergeOfficer(officers, entry) {
81
+ const existing = officers.find((other) => entry.taxCode !== undefined && other.taxCode !== undefined
82
+ ? entry.taxCode === other.taxCode
83
+ : foldText(other.name ?? '') === foldText(entry.name ?? ''));
84
+ if (!existing) {
85
+ officers.push(entry);
86
+ return;
87
+ }
88
+ if (entry.taxCode !== undefined)
89
+ existing.taxCode = entry.taxCode;
90
+ for (const role of entry.roles ?? []) {
91
+ existing.roles ??= [];
92
+ if (!existing.roles.some((other) => foldText(other) === foldText(role)))
93
+ existing.roles.push(role);
94
+ }
95
+ }
96
+ export function parsePeople(pdf, coverPage) {
97
+ const result = { officers: [], shareholders: [] };
98
+ const page = joinPages(pdf.pages);
99
+ const spans = semanticSpans(page);
100
+ const records = identities(page);
101
+ const sections = spans
102
+ .filter((span) => span.x < page.width * 0.15 && SECTION.test(normalizeText(span.text)))
103
+ .sort((a, b) => a.y - b.y);
104
+ // Longer reconstructed roles take precedence over a contained 'Presidente'.
105
+ const roles = spans
106
+ .filter((span) => OFFICER_ROLE.test(normalizeText(span.text)) ||
107
+ SHAREHOLDER_ROLE.test(normalizeText(span.text)))
108
+ .filter((span) => {
109
+ const section = sections.find((heading) => heading.y >= span.y);
110
+ return (section === undefined ||
111
+ PEOPLE_SECTION.test(normalizeText(section.text)));
112
+ })
113
+ .filter((span) => !spans.some((other) => other !== span &&
114
+ other.x <= span.x &&
115
+ Math.abs(other.y - span.y) <= 2 &&
116
+ other.width > span.width &&
117
+ other.x + other.width >= span.x + span.width &&
118
+ (OFFICER_ROLE.test(normalizeText(other.text)) ||
119
+ SHAREHOLDER_ROLE.test(normalizeText(other.text)))))
120
+ .sort((a, b) => b.y - a.y || a.x - b.x);
121
+ const roleFragments = new Set(page.spans.filter((span) => roles.some((role) => role !== span &&
122
+ Math.abs(role.x - span.x) <= 3 &&
123
+ role.y - span.y >= 0 &&
124
+ role.y - span.y <= 36 &&
125
+ normalizeText(role.text).endsWith(` ${normalizeText(span.text)}`))));
126
+ for (const role of roles) {
127
+ const text = normalizeText(role.text);
128
+ // A value beside 'carica' belongs to the preceding identity. The next
129
+ // row can be another qualification, not a new person's name.
130
+ const isDetailRole = page.spans.some((span) => span.x < role.x &&
131
+ Math.abs(span.y - role.y) <= 2 &&
132
+ /^carica$/i.test(normalizeText(span.text)));
133
+ const adjacent = isDetailRole
134
+ ? undefined
135
+ : adjacentName(page, role, roleFragments);
136
+ let identity = adjacent === undefined
137
+ ? undefined
138
+ : records.find((record) => record.name === adjacent);
139
+ if (adjacent === undefined) {
140
+ identity = records
141
+ .filter((record) => record.y > role.y &&
142
+ record.y - role.y < 220 &&
143
+ !spans.some((span) => SECTION.test(normalizeText(span.text)) &&
144
+ span.y < record.y &&
145
+ span.y > role.y) &&
146
+ !records.some((other) => other !== record &&
147
+ other.name.y < record.y &&
148
+ other.name.y > role.y))
149
+ .sort((a, b) => a.y - b.y)[0];
150
+ }
151
+ const name = adjacent ?? identity?.name;
152
+ if (name === undefined)
153
+ continue;
154
+ const person = {
155
+ name: fullName(page, name),
156
+ ...(identity ? { taxCode: identity.taxCode } : {}),
157
+ };
158
+ if (OFFICER_ROLE.test(text)) {
159
+ mergeOfficer(result.officers, { ...person, roles: [text] });
160
+ if (role.page === coverPage &&
161
+ adjacent !== undefined &&
162
+ !spans.some((span) => SECTION.test(normalizeText(span.text)) && span.y > role.y) &&
163
+ result.primaryRepresentative === undefined &&
164
+ !/^(?:sindaco|presidente del collegio)/i.test(text)) {
165
+ result.primaryRepresentative = { name: person.name, role: text };
166
+ }
167
+ }
168
+ else {
169
+ const shareholder = { ...person };
170
+ if (RIGHT.test(text))
171
+ shareholder.rightType = text;
172
+ if (/^soci[oa] unic[oa]$/i.test(text))
173
+ shareholder.isSoleShareholder = true;
174
+ if (role.page === coverPage &&
175
+ adjacent !== undefined &&
176
+ result.primaryRepresentative === undefined &&
177
+ spans.some((span) => span.page === coverPage &&
178
+ Math.abs(span.x - name.x) <= 3 &&
179
+ name.y - span.y > 0 &&
180
+ name.y - span.y <= 20 &&
181
+ /^rappresentante dell'impresa$/i.test(normalizeText(span.text))))
182
+ result.primaryRepresentative = { name: person.name, role: text };
183
+ // Quota facts must be inside this ownership record. No capital/paid-up fallback.
184
+ const nextRole = roles.find((other) => other.y < name.y - 2);
185
+ const nextSection = spans
186
+ .filter((span) => SECTION.test(normalizeText(span.text)) && span.y < name.y)
187
+ .sort((a, b) => b.y - a.y)[0];
188
+ const lower = Math.max(name.y - 180, nextRole === undefined
189
+ ? -Infinity
190
+ : nextRole.y + OWNERSHIP_ROW_TOLERANCE, nextSection?.y ?? -Infinity);
191
+ const detail = spans.filter((span) => span.y <= role.y + OWNERSHIP_ROW_TOLERANCE &&
192
+ span.y > lower &&
193
+ !SECTION.test(normalizeText(span.text)));
194
+ for (const span of detail) {
195
+ const value = normalizeText(span.text);
196
+ const nominal = /^(?:valore nominale|quota)\s*(?:di nominali)?\s*:?\s*([\d.,]+)\s*(euro|EUR)?$/i.exec(value);
197
+ if (nominal?.[1]) {
198
+ const amount = parseItalianAmount(nominal[1]);
199
+ if (amount !== undefined)
200
+ shareholder.nominalValue = amount;
201
+ if (nominal[2])
202
+ shareholder.currency = 'EUR';
203
+ }
204
+ const percentage = /^(?:percentuale(?: di partecipazione)?\s*:?\s*)?([\d.,]+)\s*%$/i.exec(value);
205
+ const amount = percentage?.[1] === undefined
206
+ ? undefined
207
+ : parseItalianAmount(percentage[1]);
208
+ if (amount !== undefined && amount <= 100)
209
+ shareholder.ownershipPercentage = amount;
210
+ const right = /^tipo (?:di )?diritto\s*:\s*(.+)$/i.exec(value);
211
+ if (right?.[1] && RIGHT.test(right[1]))
212
+ shareholder.rightType = right[1];
213
+ }
214
+ const existing = result.shareholders.find((other) => (person.taxCode !== undefined && other.taxCode !== undefined
215
+ ? other.taxCode === person.taxCode
216
+ : foldText(other.name ?? '') === foldText(person.name)) &&
217
+ (other.rightType === undefined ||
218
+ shareholder.rightType === undefined ||
219
+ foldText(other.rightType) === foldText(shareholder.rightType)) &&
220
+ (other.nominalValue === undefined ||
221
+ shareholder.nominalValue === undefined ||
222
+ other.nominalValue === shareholder.nominalValue));
223
+ if (existing)
224
+ Object.assign(existing, shareholder);
225
+ else
226
+ result.shareholders.push(shareholder);
227
+ }
228
+ }
229
+ return result;
230
+ }
231
+ //# sourceMappingURL=parse-people.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-people.js","sourceRoot":"","sources":["../../src/internal/parse-people.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { ExtractedPage, PositionedSpan } from './model.js';
2
+ export declare function normalizeText(value: string): string;
3
+ export declare function foldText(value: string): string;
4
+ export declare function unique(values: readonly string[]): string[];
5
+ export declare function spansInReadingOrder(page: ExtractedPage): PositionedSpan[];
6
+ /**
7
+ * Adds deterministic same-line phrases for PDFs that split one printed label
8
+ * into several text items. Original spans are retained for value lookup.
9
+ */
10
+ export declare function semanticSpans(page: ExtractedPage): PositionedSpan[];
11
+ export declare function pageText(page: ExtractedPage): string;
12
+ //# sourceMappingURL=text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../src/internal/text.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEhE,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMnD;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED,wBAAgB,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAa1D;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,aAAa,GAAG,cAAc,EAAE,CAMzE;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,aAAa,GAAG,cAAc,EAAE,CAkEnE;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAIpD"}
@@ -0,0 +1,99 @@
1
+ export function normalizeText(value) {
2
+ return value
3
+ .normalize('NFKC')
4
+ .replace(/[’‘`´]/g, "'")
5
+ .replace(/\s+/g, ' ')
6
+ .trim();
7
+ }
8
+ export function foldText(value) {
9
+ return normalizeText(value).toLowerCase();
10
+ }
11
+ export function unique(values) {
12
+ const seen = new Set();
13
+ const result = [];
14
+ for (const value of values) {
15
+ const normalized = normalizeText(value);
16
+ const key = foldText(normalized);
17
+ if (normalized.length === 0 || seen.has(key))
18
+ continue;
19
+ seen.add(key);
20
+ result.push(normalized);
21
+ }
22
+ return result;
23
+ }
24
+ export function spansInReadingOrder(page) {
25
+ return [...page.spans].sort((left, right) => {
26
+ const rowDifference = right.y - left.y;
27
+ if (Math.abs(rowDifference) > 2)
28
+ return rowDifference;
29
+ return left.x - right.x;
30
+ });
31
+ }
32
+ /**
33
+ * Adds deterministic same-line phrases for PDFs that split one printed label
34
+ * into several text items. Original spans are retained for value lookup.
35
+ */
36
+ export function semanticSpans(page) {
37
+ const original = spansInReadingOrder(page);
38
+ const rows = [];
39
+ for (const span of original) {
40
+ const row = rows.find((candidate) => Math.abs((candidate[0]?.y ?? span.y) - span.y) <= 2);
41
+ if (row === undefined)
42
+ rows.push([span]);
43
+ else
44
+ row.push(span);
45
+ }
46
+ const phrases = [...original];
47
+ for (const row of rows) {
48
+ row.sort((left, right) => left.x - right.x);
49
+ for (let start = 0; start < row.length; start += 1) {
50
+ const first = row[start];
51
+ if (first === undefined)
52
+ continue;
53
+ let text = first.text;
54
+ let right = first.x + first.width;
55
+ for (let end = start + 1; end < Math.min(row.length, start + 7); end += 1) {
56
+ const next = row[end];
57
+ if (next === undefined || next.x - right > 18)
58
+ break;
59
+ text = `${text} ${next.text}`;
60
+ right = Math.max(right, next.x + next.width);
61
+ phrases.push({
62
+ ...first,
63
+ text: normalizeText(text),
64
+ width: right - first.x,
65
+ height: Math.max(first.height, next.height),
66
+ hasEol: next.hasEol,
67
+ });
68
+ }
69
+ }
70
+ }
71
+ for (const first of original) {
72
+ let previous = first;
73
+ let text = first.text;
74
+ let width = first.width;
75
+ for (let depth = 0; depth < 2; depth += 1) {
76
+ const next = original.find((span) => Math.abs(span.x - first.x) <= 3 &&
77
+ previous.y - span.y > 3 &&
78
+ previous.y - span.y <= 18);
79
+ if (next === undefined)
80
+ break;
81
+ text = `${text} ${next.text}`;
82
+ width = Math.max(width, next.width);
83
+ phrases.push({
84
+ ...first,
85
+ text: normalizeText(text),
86
+ width,
87
+ hasEol: next.hasEol,
88
+ });
89
+ previous = next;
90
+ }
91
+ }
92
+ return phrases;
93
+ }
94
+ export function pageText(page) {
95
+ return spansInReadingOrder(page)
96
+ .map((span) => span.text)
97
+ .join('\n');
98
+ }
99
+ //# sourceMappingURL=text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/internal/text.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export declare function parseItalianDate(value: string): string | undefined;
2
+ export declare function parseItalianAmount(value: string): number | undefined;
3
+ export declare function parseItalianCount(value: string): number | undefined;
4
+ export declare function parseItalianBoolean(value: string): boolean | undefined;
5
+ //# sourceMappingURL=values.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"values.d.ts","sourceRoot":"","sources":["../../src/internal/values.ts"],"names":[],"mappings":"AAQA,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAyBlE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAepE;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAMnE;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAKtE"}
@@ -0,0 +1,64 @@
1
+ const ITALIAN_DATE = /^(\d{2})\/(\d{2})\/(\d{4})$/;
2
+ const ITALIAN_AMOUNT = /^(?:\d+|\d{1,3}(?:\.\d{3})+)(?:,\d{1,2})?$/;
3
+ const ITALIAN_INTEGER = /^(?:\d+|\d{1,3}(?:\.\d{3})+)$/;
4
+ function isLeapYear(year) {
5
+ return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
6
+ }
7
+ export function parseItalianDate(value) {
8
+ const match = ITALIAN_DATE.exec(value.trim());
9
+ if (match === null)
10
+ return undefined;
11
+ const day = Number(match[1]);
12
+ const month = Number(match[2]);
13
+ const year = Number(match[3]);
14
+ const monthLengths = [
15
+ 31,
16
+ isLeapYear(year) ? 29 : 28,
17
+ 31,
18
+ 30,
19
+ 31,
20
+ 30,
21
+ 31,
22
+ 31,
23
+ 30,
24
+ 31,
25
+ 30,
26
+ 31,
27
+ ];
28
+ const maximumDay = monthLengths[month - 1];
29
+ if (maximumDay === undefined || day < 1 || day > maximumDay)
30
+ return undefined;
31
+ return `${String(year).padStart(4, '0')}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
32
+ }
33
+ export function parseItalianAmount(value) {
34
+ const normalized = value.trim();
35
+ if (!ITALIAN_AMOUNT.test(normalized))
36
+ return undefined;
37
+ const [integerPart = '', decimalPart = ''] = normalized.split(',');
38
+ const centsText = `${integerPart.replaceAll('.', '')}${decimalPart.padEnd(2, '0')}`;
39
+ const cents = Number(centsText);
40
+ if (!Number.isSafeInteger(cents))
41
+ return undefined;
42
+ const amount = cents / 100;
43
+ // JSON uses the shortest decimal representation. Reject a value when that
44
+ // representation would change a source cent at the upper end of the range.
45
+ const [whole = '', fraction = ''] = String(amount).split('.');
46
+ const serializedCents = BigInt(`${whole}${fraction.padEnd(2, '0')}`);
47
+ return serializedCents === BigInt(centsText) ? amount : undefined;
48
+ }
49
+ export function parseItalianCount(value) {
50
+ const normalized = value.trim();
51
+ if (!ITALIAN_INTEGER.test(normalized))
52
+ return undefined;
53
+ const count = Number(normalized.replaceAll('.', ''));
54
+ return Number.isSafeInteger(count) ? count : undefined;
55
+ }
56
+ export function parseItalianBoolean(value) {
57
+ const normalized = value.trim().toLowerCase();
58
+ if (normalized === 'si' || normalized === 'sì')
59
+ return true;
60
+ if (normalized === 'no')
61
+ return false;
62
+ return undefined;
63
+ }
64
+ //# sourceMappingURL=values.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"values.js","sourceRoot":"","sources":["../../src/internal/values.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { ParseVisuraOptions, VisuraDocument } from './types.js';
2
+ export declare function parseVisura(input: Uint8Array, options?: ParseVisuraOptions): Promise<VisuraDocument>;
3
+ //# sourceMappingURL=parse-visura.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-visura.d.ts","sourceRoot":"","sources":["../src/parse-visura.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAErE,wBAAsB,WAAW,CAC/B,KAAK,EAAE,UAAU,EACjB,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,cAAc,CAAC,CAsBzB"}
@@ -0,0 +1,17 @@
1
+ import { extractPdf } from './internal/extract-pdf.js';
2
+ import { isSupportedVisuraBlock, parseExtractedDocument, } from './internal/parse-document.js';
3
+ import { VisuraParseError } from './errors.js';
4
+ export async function parseVisura(input, options = {}) {
5
+ const pdf = await extractPdf(input);
6
+ const result = parseExtractedDocument(pdf, options.filename);
7
+ if (result.reportType === undefined && !isSupportedVisuraBlock(pdf)) {
8
+ throw new VisuraParseError('unsupported-pdf', 'The PDF is text-based but is not a recognized Visura camerale.');
9
+ }
10
+ if (result.companyName === undefined &&
11
+ result.taxCode === undefined &&
12
+ result.reaNumber === undefined) {
13
+ throw new VisuraParseError('malformed-content', 'The Visura type was recognized, but no subject identity could be parsed.');
14
+ }
15
+ return result;
16
+ }
17
+ //# sourceMappingURL=parse-visura.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-visura.js","sourceRoot":"","sources":["../src/parse-visura.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EACL,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAG/C,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,KAAiB,EACjB,UAA8B,EAAE;IAEhC,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,CAAC;IACpC,MAAM,MAAM,GAAG,sBAAsB,CAAC,GAAG,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IAE7D,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,EAAE,CAAC;QACpE,MAAM,IAAI,gBAAgB,CACxB,iBAAiB,EACjB,gEAAgE,CACjE,CAAC;IACJ,CAAC;IACD,IACE,MAAM,CAAC,WAAW,KAAK,SAAS;QAChC,MAAM,CAAC,OAAO,KAAK,SAAS;QAC5B,MAAM,CAAC,SAAS,KAAK,SAAS,EAC9B,CAAC;QACD,MAAM,IAAI,gBAAgB,CACxB,mBAAmB,EACnB,0EAA0E,CAC3E,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}