@flowselections/layout-maker 1.38.4 → 1.45.9

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 (110) hide show
  1. package/dist-lib/components/AfleverbriefBuilderPage.d.ts +10 -0
  2. package/dist-lib/components/AfleverbriefBuilderPage.d.ts.map +1 -0
  3. package/dist-lib/components/AfleverbriefBuilderPage.js +101 -0
  4. package/dist-lib/components/BlokOntwerperPage.d.ts.map +1 -1
  5. package/dist-lib/components/BlokOntwerperPage.js +8 -3
  6. package/dist-lib/components/ComputersLijst.d.ts.map +1 -1
  7. package/dist-lib/components/ComputersLijst.js +1 -1
  8. package/dist-lib/components/FustKolomVolgorde.d.ts +11 -0
  9. package/dist-lib/components/FustKolomVolgorde.d.ts.map +1 -0
  10. package/dist-lib/components/FustKolomVolgorde.js +52 -0
  11. package/dist-lib/components/LayoutsPage.d.ts.map +1 -1
  12. package/dist-lib/components/LayoutsPage.js +10 -9
  13. package/dist-lib/components/PakbonBuilderPage.d.ts.map +1 -1
  14. package/dist-lib/components/PakbonBuilderPage.js +32 -14
  15. package/dist-lib/components/PakbonPreview.d.ts.map +1 -1
  16. package/dist-lib/components/PakbonPreview.js +21 -14
  17. package/dist-lib/components/PakbonSectiesPanel.d.ts +4 -2
  18. package/dist-lib/components/PakbonSectiesPanel.d.ts.map +1 -1
  19. package/dist-lib/components/PakbonSectiesPanel.js +5 -3
  20. package/dist-lib/components/PdfMapToestemmingBanner.d.ts +2 -1
  21. package/dist-lib/components/PdfMapToestemmingBanner.d.ts.map +1 -1
  22. package/dist-lib/components/PdfMapToestemmingBanner.js +124 -18
  23. package/dist-lib/components/PdfVoorbeeld.d.ts +20 -0
  24. package/dist-lib/components/PdfVoorbeeld.d.ts.map +1 -0
  25. package/dist-lib/components/PdfVoorbeeld.js +95 -0
  26. package/dist-lib/components/PrintProgrammaUitleg.d.ts.map +1 -1
  27. package/dist-lib/components/PrintProgrammaUitleg.js +1 -1
  28. package/dist-lib/components/PrintQueuePage.d.ts.map +1 -1
  29. package/dist-lib/components/PrintQueuePage.js +81 -43
  30. package/dist-lib/components/PrintplekDetailPage.d.ts.map +1 -1
  31. package/dist-lib/components/PrintplekDetailPage.js +2 -2
  32. package/dist-lib/components/PrintplekWizard.d.ts.map +1 -1
  33. package/dist-lib/components/PrintplekWizard.js +6 -2
  34. package/dist-lib/components/ProductiebonBuilderPage.d.ts.map +1 -1
  35. package/dist-lib/components/ProductiebonBuilderPage.js +13 -7
  36. package/dist-lib/index.d.ts +13 -5
  37. package/dist-lib/index.d.ts.map +1 -1
  38. package/dist-lib/index.js +10 -5
  39. package/dist-lib/integrations/supabase/auth-middleware.d.ts +2280 -198
  40. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  41. package/dist-lib/integrations/supabase/client.server.d.ts +2280 -198
  42. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  43. package/dist-lib/integrations/supabase/client.server.js +1 -1
  44. package/dist-lib/integrations/supabase/moduleClient.d.ts +2280 -198
  45. package/dist-lib/integrations/supabase/moduleClient.d.ts.map +1 -1
  46. package/dist-lib/integrations/supabase/types.d.ts +2306 -182
  47. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  48. package/dist-lib/integrations/supabase/types.js +1 -0
  49. package/dist-lib/lib/aiLayout.functions.d.ts +2280 -198
  50. package/dist-lib/lib/aiLayout.functions.d.ts.map +1 -1
  51. package/dist-lib/lib/documentVoorbeeld.functions.d.ts +23 -0
  52. package/dist-lib/lib/documentVoorbeeld.functions.d.ts.map +1 -0
  53. package/dist-lib/lib/documentVoorbeeld.functions.js +37 -0
  54. package/dist-lib/lib/fontMetrics.d.ts +15 -0
  55. package/dist-lib/lib/fontMetrics.d.ts.map +1 -0
  56. package/dist-lib/lib/fontMetrics.js +118 -0
  57. package/dist-lib/lib/htmlNaarPdf.server.d.ts +2 -2
  58. package/dist-lib/lib/htmlNaarPdf.server.d.ts.map +1 -1
  59. package/dist-lib/lib/htmlNaarPdf.server.js +2 -1
  60. package/dist-lib/lib/htmlPdf.server.d.ts +11 -0
  61. package/dist-lib/lib/htmlPdf.server.d.ts.map +1 -1
  62. package/dist-lib/lib/htmlPdf.server.js +118 -15
  63. package/dist-lib/lib/orderData.server.d.ts.map +1 -1
  64. package/dist-lib/lib/orderData.server.js +43 -16
  65. package/dist-lib/lib/pakbonBarcodeVerzoek.d.ts +39 -0
  66. package/dist-lib/lib/pakbonBarcodeVerzoek.d.ts.map +1 -0
  67. package/dist-lib/lib/pakbonBarcodeVerzoek.js +80 -0
  68. package/dist-lib/lib/pakbonBuilder.d.ts +62 -3
  69. package/dist-lib/lib/pakbonBuilder.d.ts.map +1 -1
  70. package/dist-lib/lib/pakbonBuilder.js +386 -50
  71. package/dist-lib/lib/pakbonContent.d.ts +41 -0
  72. package/dist-lib/lib/pakbonContent.d.ts.map +1 -0
  73. package/dist-lib/lib/pakbonContent.js +668 -0
  74. package/dist-lib/lib/pakbonDocument.d.ts +199 -0
  75. package/dist-lib/lib/pakbonDocument.d.ts.map +1 -0
  76. package/dist-lib/lib/pakbonDocument.js +735 -0
  77. package/dist-lib/lib/pakbonHtml.d.ts +7 -0
  78. package/dist-lib/lib/pakbonHtml.d.ts.map +1 -1
  79. package/dist-lib/lib/pakbonHtml.js +89 -73
  80. package/dist-lib/lib/pakbonPresentation.d.ts +8 -0
  81. package/dist-lib/lib/pakbonPresentation.d.ts.map +1 -0
  82. package/dist-lib/lib/pakbonPresentation.js +31 -0
  83. package/dist-lib/lib/pakbonScan.d.ts.map +1 -1
  84. package/dist-lib/lib/pakbonScan.js +20 -38
  85. package/dist-lib/lib/pdfJobSaver.d.ts +10 -6
  86. package/dist-lib/lib/pdfJobSaver.d.ts.map +1 -1
  87. package/dist-lib/lib/pdfJobSaver.js +302 -99
  88. package/dist-lib/lib/pdfMap.d.ts +37 -2
  89. package/dist-lib/lib/pdfMap.d.ts.map +1 -1
  90. package/dist-lib/lib/pdfMap.js +120 -7
  91. package/dist-lib/lib/pdfOpslagStatus.d.ts +23 -0
  92. package/dist-lib/lib/pdfOpslagStatus.d.ts.map +1 -0
  93. package/dist-lib/lib/pdfOpslagStatus.js +74 -0
  94. package/dist-lib/lib/printApi.d.ts +31 -0
  95. package/dist-lib/lib/printApi.d.ts.map +1 -1
  96. package/dist-lib/lib/printApi.js +62 -1
  97. package/dist-lib/lib/printQueue.server.d.ts +6 -0
  98. package/dist-lib/lib/printQueue.server.d.ts.map +1 -1
  99. package/dist-lib/lib/printQueue.server.js +450 -94
  100. package/dist-lib/lib/printService.functions.d.ts +8857 -529
  101. package/dist-lib/lib/printService.functions.d.ts.map +1 -1
  102. package/dist-lib/lib/profielHtml.d.ts.map +1 -1
  103. package/dist-lib/lib/profielHtml.js +8 -1
  104. package/dist-lib/lib/useDocumentData.d.ts +8 -0
  105. package/dist-lib/lib/useDocumentData.d.ts.map +1 -1
  106. package/dist-lib/lib/useDocumentData.js +1 -1
  107. package/dist-lib/lib/voorbeeldPrint.d.ts +3 -0
  108. package/dist-lib/lib/voorbeeldPrint.d.ts.map +1 -1
  109. package/dist-lib/lib/voorbeeldPrint.js +65 -17
  110. package/package.json +9 -2
@@ -0,0 +1,735 @@
1
+ // ============================================================================
2
+ // src/lib/pakbonDocument.ts — het DocumentModel en de centrale layout-engine.
3
+ // ----------------------------------------------------------------------------
4
+ // Hier — en NERGENS anders — wordt bepaald waar iets op de pakbon terechtkomt:
5
+ // pagina, x, y, breedte, hoogte, fontgrootte, regelafbreking, kolombreedtes,
6
+ // rijhoogtes en pagina-einden. Alle uitvoerkanalen (React-voorbeeld, HTML en
7
+ // PDF) zijn "domme" renderers die uitsluitend dit model natekenen.
8
+ //
9
+ // inhoudsmodel (Flow[]) -> layout-engine -> DocumentModel (mm) -> renderers
10
+ // ============================================================================
11
+ import { breekAf, regelHoogtePt, tekstBreedtePt } from "./fontMetrics.js";
12
+ export const PT_PER_MM = 2.834645669291339;
13
+ export const mmNaarPt = (mm) => mm * PT_PER_MM;
14
+ export const ptNaarMm = (pt) => pt / PT_PER_MM;
15
+ export const DOCUMENT_MODEL_VERSIE = 1;
16
+ export const KLEUR_DONKER = "#111827";
17
+ export const KLEUR_GRIJS = "#6b7280";
18
+ export const KLEUR_MIDDEN = "#4b5563";
19
+ export const KLEUR_LIJN = "#d1d5db";
20
+ class Layouter {
21
+ constructor(o) {
22
+ this.o = o;
23
+ this.paginas = [];
24
+ this.y = 0;
25
+ this.kopHoogte = 0;
26
+ this.kop = [];
27
+ this.links = o.margeMm;
28
+ this.breedte = o.breedteMm - o.margeMm * 2;
29
+ this.bovenkant = o.margeMm;
30
+ this.onderkant = o.hoogteMm - o.margeMm;
31
+ }
32
+ zetVoetHoogte(hoogteMm) {
33
+ this.onderkant = this.o.hoogteMm - this.o.margeMm - hoogteMm;
34
+ }
35
+ zetKop(kop) {
36
+ this.kop = kop;
37
+ this.kopHoogte = kop.length ? this.meet(kop, this.breedte) : 0;
38
+ }
39
+ get ruimte() {
40
+ return this.onderkant - this.y;
41
+ }
42
+ get paginaHoogte() {
43
+ return this.onderkant - this.bovenkant - this.kopHoogte;
44
+ }
45
+ huidige() {
46
+ if (!this.paginas.length)
47
+ this.nieuwePagina();
48
+ return this.paginas[this.paginas.length - 1];
49
+ }
50
+ nieuwePagina() {
51
+ this.paginas.push([]);
52
+ this.y = this.bovenkant;
53
+ if (this.kop.length) {
54
+ const pagina = this.paginas[this.paginas.length - 1];
55
+ for (const f of this.kop) {
56
+ const g = maakBlok(f, this.links, this.breedte, this.o.basisGroottePt);
57
+ pagina.push(...g.teken(this.y));
58
+ this.y += g.hoogteMm;
59
+ }
60
+ }
61
+ }
62
+ zorgVoor(hoogteMm) {
63
+ if (!this.paginas.length)
64
+ this.nieuwePagina();
65
+ else if (hoogteMm > this.ruimte && this.y > this.bovenkant + this.kopHoogte + 0.01)
66
+ this.nieuwePagina();
67
+ }
68
+ emit(g) {
69
+ this.zorgVoor(g.hoogteMm);
70
+ this.huidige().push(...g.teken(this.y));
71
+ this.y += g.hoogteMm;
72
+ }
73
+ meet(flows, breedte) {
74
+ let h = 0;
75
+ for (const f of flows) {
76
+ if (f.kind === "paginabreuk")
77
+ continue;
78
+ h += maakBlok(f, 0, breedte, this.o.basisGroottePt).hoogteMm;
79
+ }
80
+ return h;
81
+ }
82
+ plaats(flows) {
83
+ for (const f of flows)
84
+ this.plaatsEen(f);
85
+ }
86
+ plaatsEen(f) {
87
+ if (f.kind === "paginabreuk") {
88
+ if (this.paginas.length && this.y > this.bovenkant + this.kopHoogte + 0.01)
89
+ this.nieuwePagina();
90
+ else if (!this.paginas.length)
91
+ this.nieuwePagina();
92
+ return;
93
+ }
94
+ if (!this.paginas.length)
95
+ this.nieuwePagina();
96
+ if (f.kind === "tabel") {
97
+ this.plaatsTabel(f, this.links, this.breedte);
98
+ return;
99
+ }
100
+ if (f.kind === "groep") {
101
+ this.plaatsGroep(f);
102
+ return;
103
+ }
104
+ this.emit(maakBlok(f, this.links, this.breedte, this.o.basisGroottePt));
105
+ }
106
+ plaatsGroep(g) {
107
+ const breedte = (this.breedte * Math.min(Math.max(g.breedtePercent ?? 100, 5), 100)) / 100;
108
+ const x = g.uitlijning === "right"
109
+ ? this.links + this.breedte - breedte
110
+ : g.uitlijning === "center"
111
+ ? this.links + (this.breedte - breedte) / 2
112
+ : this.links;
113
+ const padding = g.paddingMm ?? (g.kader ? 1.5 : 0);
114
+ const binnenBreedte = breedte - padding * 2;
115
+ const hoogte = this.meet(g.kinderen, binnenBreedte) + padding * 2;
116
+ const bijElkaar = g.bijElkaar === true;
117
+ if (bijElkaar && hoogte <= this.paginaHoogte)
118
+ this.zorgVoor(hoogte);
119
+ const start = this.y;
120
+ const startPagina = this.paginas.length;
121
+ if (padding)
122
+ this.y += padding;
123
+ const buiten = { links: this.links, breedte: this.breedte };
124
+ this.links = x + padding;
125
+ this.breedte = binnenBreedte;
126
+ for (const kind of g.kinderen)
127
+ this.plaatsEen(kind);
128
+ this.links = buiten.links;
129
+ this.breedte = buiten.breedte;
130
+ if (padding)
131
+ this.y += padding;
132
+ // Kader/achtergrond alleen wanneer de groep binnen één pagina bleef.
133
+ if ((g.kader || g.achtergrond) && this.paginas.length === startPagina) {
134
+ this.huidige().unshift({
135
+ type: "vlak",
136
+ xMm: x,
137
+ yMm: start,
138
+ breedteMm: breedte,
139
+ hoogteMm: this.y - start,
140
+ vulling: g.achtergrond,
141
+ randKleur: g.kader ? KLEUR_LIJN : undefined,
142
+ randMm: g.kader ? 0.25 : undefined,
143
+ });
144
+ }
145
+ this.y += g.margeOnderMm ?? 0;
146
+ }
147
+ plaatsTabel(t, x, breedte) {
148
+ const grootte = t.groottePt ?? this.o.basisGroottePt;
149
+ const kopGrootte = t.kopGroottePt ?? grootte * 0.9;
150
+ const breedtes = kolomBreedtesMm(t.kolommen, breedte);
151
+ const kopRij = t.toonKop !== false ? kopRijHoogte(t, breedtes, kopGrootte) : null;
152
+ const randen = t.randen ?? "onder";
153
+ if (!this.paginas.length)
154
+ this.nieuwePagina();
155
+ let kopGetekend = false;
156
+ const tekenKop = () => {
157
+ if (!kopRij)
158
+ return;
159
+ this.zorgVoor(kopRij.hoogte + rijHoogte(t, 0, breedtes, grootte));
160
+ this.huidige().push(...kopRij.teken(this.y, x, breedtes, randen));
161
+ this.y += kopRij.hoogte;
162
+ kopGetekend = true;
163
+ };
164
+ tekenKop();
165
+ for (let i = 0; i < t.rijen.length; i++) {
166
+ const h = rijHoogte(t, i, breedtes, grootte);
167
+ if (h > this.ruimte && this.y > this.bovenkant + this.kopHoogte + 0.01) {
168
+ this.nieuwePagina();
169
+ if (kopRij && t.kopHerhalen !== false && kopGetekend) {
170
+ this.huidige().push(...kopRij.teken(this.y, x, breedtes, randen));
171
+ this.y += kopRij.hoogte;
172
+ }
173
+ }
174
+ this.huidige().push(...tekenRij(t, i, this.y, x, breedtes, grootte, randen));
175
+ this.y += h;
176
+ }
177
+ }
178
+ }
179
+ // --- 4. blokken meten en tekenen ---------------------------------------------
180
+ function kolomBreedtesMm(kolommen, breedteMm) {
181
+ const ruw = kolommen.map((k) => (Number(k.breedte) > 0 ? Number(k.breedte) : 0));
182
+ const som = ruw.reduce((a, b) => a + b, 0);
183
+ const leeg = ruw.filter((b) => b === 0).length;
184
+ const rest = Math.max(0, 100 - som);
185
+ const per = leeg ? rest / leeg : 0;
186
+ const gevuld = ruw.map((b) => (b === 0 ? per : b));
187
+ const totaal = gevuld.reduce((a, b) => a + b, 0) || 1;
188
+ return gevuld.map((b) => (b / totaal) * breedteMm);
189
+ }
190
+ const CEL_PAD_X = 1;
191
+ const CEL_PAD_Y = 0.7;
192
+ function celRegels(tekst, breedteMm, groottePt, vet) {
193
+ const max = mmNaarPt(Math.max(breedteMm - CEL_PAD_X * 2, 1));
194
+ return breekAf(tekst, max, groottePt, vet);
195
+ }
196
+ function rijHoogte(t, index, breedtes, groottePt) {
197
+ const rij = t.rijen[index] ?? [];
198
+ let regels = 1;
199
+ t.kolommen.forEach((k, i) => {
200
+ const n = celRegels(String(rij[i] ?? ""), breedtes[i] ?? 0, k.groottePt ?? groottePt, !!k.vet).length;
201
+ if (n > regels)
202
+ regels = n;
203
+ });
204
+ return ptNaarMm(regelHoogtePt(groottePt)) * regels + CEL_PAD_Y * 2;
205
+ }
206
+ function tekenRij(t, index, yMm, xMm, breedtes, groottePt, randen) {
207
+ const rij = t.rijen[index] ?? [];
208
+ const hoogte = rijHoogte(t, index, breedtes, groottePt);
209
+ const uit = [];
210
+ if (t.zebra && index % 2 === 1)
211
+ uit.push({
212
+ type: "vlak",
213
+ xMm,
214
+ yMm,
215
+ breedteMm: breedtes.reduce((a, b) => a + b, 0),
216
+ hoogteMm: hoogte,
217
+ vulling: "#f3f4f6",
218
+ });
219
+ let x = xMm;
220
+ t.kolommen.forEach((k, i) => {
221
+ const b = breedtes[i] ?? 0;
222
+ const grootte = k.groottePt ?? groottePt;
223
+ const regels = celRegels(String(rij[i] ?? ""), b, grootte, !!k.vet);
224
+ uit.push({
225
+ type: "tekst",
226
+ xMm: x + CEL_PAD_X,
227
+ yMm: yMm + CEL_PAD_Y,
228
+ breedteMm: b - CEL_PAD_X * 2,
229
+ regels,
230
+ groottePt: grootte,
231
+ vet: !!k.vet,
232
+ kleur: KLEUR_DONKER,
233
+ uitlijning: k.uitlijning ?? "left",
234
+ regelhoogteMm: ptNaarMm(regelHoogtePt(grootte)),
235
+ });
236
+ if (randen === "rand")
237
+ uit.push({
238
+ type: "vlak",
239
+ xMm: x,
240
+ yMm,
241
+ breedteMm: b,
242
+ hoogteMm: hoogte,
243
+ randKleur: KLEUR_LIJN,
244
+ randMm: 0.2,
245
+ });
246
+ x += b;
247
+ });
248
+ if (randen === "onder")
249
+ uit.push({
250
+ type: "lijn",
251
+ xMm,
252
+ yMm: yMm + hoogte,
253
+ breedteMm: breedtes.reduce((a, b) => a + b, 0),
254
+ dikteMm: 0.2,
255
+ kleur: KLEUR_LIJN,
256
+ });
257
+ return uit;
258
+ }
259
+ function kopRijHoogte(t, breedtes, kopGrootte) {
260
+ let regels = 1;
261
+ t.kolommen.forEach((k, i) => {
262
+ const n = celRegels(k.label, breedtes[i] ?? 0, kopGrootte, true).length;
263
+ if (n > regels)
264
+ regels = n;
265
+ });
266
+ const hoogte = ptNaarMm(regelHoogtePt(kopGrootte)) * regels + CEL_PAD_Y * 2;
267
+ return {
268
+ hoogte,
269
+ teken: (yMm, xMm, br, randen) => {
270
+ const uit = [];
271
+ let x = xMm;
272
+ t.kolommen.forEach((k, i) => {
273
+ const b = br[i] ?? 0;
274
+ uit.push({
275
+ type: "tekst",
276
+ xMm: x + CEL_PAD_X,
277
+ yMm: yMm + CEL_PAD_Y,
278
+ breedteMm: b - CEL_PAD_X * 2,
279
+ regels: celRegels(k.label.toUpperCase(), b, kopGrootte, true),
280
+ groottePt: kopGrootte,
281
+ vet: true,
282
+ kleur: KLEUR_GRIJS,
283
+ uitlijning: k.uitlijning ?? "left",
284
+ regelhoogteMm: ptNaarMm(regelHoogtePt(kopGrootte)),
285
+ });
286
+ if (randen === "rand")
287
+ uit.push({ type: "vlak", xMm: x, yMm, breedteMm: b, hoogteMm: hoogte, randKleur: KLEUR_LIJN, randMm: 0.2 });
288
+ x += b;
289
+ });
290
+ if (randen === "onder")
291
+ uit.push({
292
+ type: "lijn",
293
+ xMm,
294
+ yMm: yMm + hoogte,
295
+ breedteMm: br.reduce((a, b) => a + b, 0),
296
+ dikteMm: 0.2,
297
+ kleur: KLEUR_LIJN,
298
+ });
299
+ return uit;
300
+ },
301
+ };
302
+ }
303
+ function tekstBlok(tekst, x, breedte, groottePt, opties = {}) {
304
+ const regels = breekAf(tekst, mmNaarPt(breedte), groottePt, !!opties.vet);
305
+ const rh = ptNaarMm(regelHoogtePt(groottePt));
306
+ const marge = opties.margeOnderMm ?? 0;
307
+ return {
308
+ hoogteMm: rh * regels.length + marge,
309
+ teken: (y) => [
310
+ {
311
+ type: "tekst",
312
+ xMm: x,
313
+ yMm: y,
314
+ breedteMm: breedte,
315
+ regels,
316
+ groottePt,
317
+ vet: !!opties.vet,
318
+ kleur: opties.kleur ?? KLEUR_DONKER,
319
+ uitlijning: opties.uitlijning ?? "left",
320
+ regelhoogteMm: rh,
321
+ },
322
+ ],
323
+ };
324
+ }
325
+ /** Zet één inhoudsblok om naar meetbare/tekenbare uitvoer op vaste positie. */
326
+ function maakBlok(f, x, breedte, basis) {
327
+ switch (f.kind) {
328
+ case "tekst":
329
+ return tekstBlok(f.tekst, x, breedte, f.groottePt ?? basis, {
330
+ vet: f.vet,
331
+ kleur: f.kleur,
332
+ uitlijning: f.uitlijning,
333
+ margeOnderMm: f.margeOnderMm ?? 0,
334
+ });
335
+ case "band": {
336
+ const grootte = f.groottePt ?? basis;
337
+ const metaGrootte = f.metaGroottePt ?? grootte * 0.78;
338
+ const padX = 1.5;
339
+ const padY = 0.8;
340
+ const titelRegels = breekAf(f.titel, mmNaarPt(breedte - padX * 2), grootte, true);
341
+ const metaRegels = f.meta ? breekAf(f.meta, mmNaarPt(breedte - padX * 2), metaGrootte, false) : [];
342
+ const rhT = ptNaarMm(regelHoogtePt(grootte));
343
+ const rhM = ptNaarMm(regelHoogtePt(metaGrootte));
344
+ const inhoud = rhT * titelRegels.length + rhM * metaRegels.length;
345
+ const hoogte = inhoud + padY * 2 + (f.margeOnderMm ?? 1);
346
+ return {
347
+ hoogteMm: hoogte,
348
+ teken: (y) => {
349
+ const uit = [];
350
+ if (f.achtergrond)
351
+ uit.push({
352
+ type: "vlak",
353
+ xMm: x,
354
+ yMm: y,
355
+ breedteMm: breedte,
356
+ hoogteMm: inhoud + padY * 2,
357
+ vulling: f.achtergrond,
358
+ });
359
+ if (f.lijnBovenOnder) {
360
+ uit.push({ type: "lijn", xMm: x, yMm: y, breedteMm: breedte, dikteMm: 0.6, kleur: KLEUR_DONKER });
361
+ uit.push({
362
+ type: "lijn",
363
+ xMm: x,
364
+ yMm: y + inhoud + padY * 2,
365
+ breedteMm: breedte,
366
+ dikteMm: 0.6,
367
+ kleur: KLEUR_DONKER,
368
+ });
369
+ }
370
+ uit.push({
371
+ type: "tekst",
372
+ xMm: x + padX,
373
+ yMm: y + padY,
374
+ breedteMm: breedte - padX * 2,
375
+ regels: titelRegels,
376
+ groottePt: grootte,
377
+ vet: true,
378
+ kleur: KLEUR_DONKER,
379
+ uitlijning: f.uitlijning ?? "left",
380
+ regelhoogteMm: rhT,
381
+ });
382
+ if (metaRegels.length)
383
+ uit.push({
384
+ type: "tekst",
385
+ xMm: x + padX,
386
+ yMm: y + padY + rhT * titelRegels.length,
387
+ breedteMm: breedte - padX * 2,
388
+ regels: metaRegels,
389
+ groottePt: metaGrootte,
390
+ vet: false,
391
+ kleur: KLEUR_MIDDEN,
392
+ uitlijning: f.uitlijning ?? "left",
393
+ regelhoogteMm: rhM,
394
+ });
395
+ return uit;
396
+ },
397
+ };
398
+ }
399
+ case "kv": {
400
+ const grootte = f.groottePt ?? basis;
401
+ const rh = ptNaarMm(regelHoogtePt(grootte));
402
+ const labelBreedte = Math.min(breedte * 0.55, ptNaarMm(tekstBreedtePt(f.label, grootte, false)) + 1);
403
+ const waardeRegels = breekAf(f.waarde, mmNaarPt(breedte - labelBreedte - 2), grootte, true);
404
+ return {
405
+ hoogteMm: rh * Math.max(1, waardeRegels.length),
406
+ teken: (y) => [
407
+ {
408
+ type: "tekst",
409
+ xMm: x,
410
+ yMm: y,
411
+ breedteMm: labelBreedte,
412
+ regels: [f.label],
413
+ groottePt: grootte,
414
+ vet: false,
415
+ kleur: KLEUR_GRIJS,
416
+ uitlijning: "left",
417
+ regelhoogteMm: rh,
418
+ },
419
+ {
420
+ type: "tekst",
421
+ xMm: x + labelBreedte + 2,
422
+ yMm: y,
423
+ breedteMm: breedte - labelBreedte - 2,
424
+ regels: waardeRegels,
425
+ groottePt: grootte,
426
+ vet: true,
427
+ kleur: KLEUR_DONKER,
428
+ uitlijning: "right",
429
+ regelhoogteMm: rh,
430
+ },
431
+ ],
432
+ };
433
+ }
434
+ case "kvgrid": {
435
+ const kolommen = Math.max(1, f.kolommen);
436
+ const kolomBreedte = (breedte - 6 * (kolommen - 1)) / kolommen;
437
+ const cellen = f.items.map((it) => maakBlok({ kind: "kv", label: it.label, waarde: it.waarde, groottePt: f.groottePt }, 0, kolomBreedte, basis));
438
+ const rijen = [];
439
+ for (let i = 0; i < cellen.length; i += kolommen)
440
+ rijen.push(cellen.map((_, j) => j).slice(i, i + kolommen));
441
+ const rijHoogtes = rijen.map((r) => Math.max(...r.map((i) => cellen[i].hoogteMm), 0));
442
+ return {
443
+ hoogteMm: rijHoogtes.reduce((a, b) => a + b, 0),
444
+ teken: (y) => {
445
+ const uit = [];
446
+ let cy = y;
447
+ rijen.forEach((r, ri) => {
448
+ r.forEach((idx, ci) => {
449
+ const item = f.items[idx];
450
+ const cx = x + ci * (kolomBreedte + 6);
451
+ const blok = maakBlok({ kind: "kv", label: item.label, waarde: item.waarde, groottePt: f.groottePt }, cx, kolomBreedte, basis);
452
+ uit.push(...blok.teken(cy));
453
+ });
454
+ cy += rijHoogtes[ri] ?? 0;
455
+ });
456
+ return uit;
457
+ },
458
+ };
459
+ }
460
+ case "tussen": {
461
+ const grootte = f.groottePt ?? basis;
462
+ const rh = ptNaarMm(regelHoogtePt(grootte));
463
+ const padOnder = 0.5;
464
+ const hoogte = rh + padOnder + (f.lijnOnder ? 0.6 : 0) + (f.lijnBoven ? 1.2 : 0);
465
+ return {
466
+ hoogteMm: hoogte,
467
+ teken: (y) => {
468
+ const uit = [];
469
+ let ty = y;
470
+ if (f.lijnBoven) {
471
+ uit.push({ type: "lijn", xMm: x, yMm: y, breedteMm: breedte, dikteMm: 0.25, kleur: KLEUR_LIJN });
472
+ ty += 1.2;
473
+ }
474
+ uit.push({
475
+ type: "tekst",
476
+ xMm: x,
477
+ yMm: ty,
478
+ breedteMm: breedte * 0.7,
479
+ regels: [f.links],
480
+ groottePt: grootte,
481
+ vet: f.vet !== false,
482
+ kleur: KLEUR_DONKER,
483
+ uitlijning: "left",
484
+ regelhoogteMm: rh,
485
+ });
486
+ uit.push({
487
+ type: "tekst",
488
+ xMm: x + breedte * 0.7,
489
+ yMm: ty,
490
+ breedteMm: breedte * 0.3,
491
+ regels: [f.rechts],
492
+ groottePt: grootte,
493
+ vet: f.vet !== false,
494
+ kleur: KLEUR_DONKER,
495
+ uitlijning: "right",
496
+ regelhoogteMm: rh,
497
+ });
498
+ if (f.lijnOnder)
499
+ uit.push({
500
+ type: "lijn",
501
+ xMm: x,
502
+ yMm: ty + rh + padOnder,
503
+ breedteMm: breedte,
504
+ dikteMm: 0.45,
505
+ kleur: KLEUR_DONKER,
506
+ });
507
+ return uit;
508
+ },
509
+ };
510
+ }
511
+ case "barcode": {
512
+ const grootte = basis * 0.85;
513
+ const rh = ptNaarMm(regelHoogtePt(grootte));
514
+ const toonWaarde = f.toonWaarde !== false;
515
+ const bx = f.uitlijning === "right"
516
+ ? x + breedte - f.breedteMm
517
+ : f.uitlijning === "left"
518
+ ? x
519
+ : x + (breedte - f.breedteMm) / 2;
520
+ return {
521
+ hoogteMm: f.hoogteMm + (toonWaarde ? rh : 0) + 1.5,
522
+ teken: (y) => {
523
+ const uit = [
524
+ {
525
+ type: "barcode",
526
+ xMm: bx,
527
+ yMm: y,
528
+ breedteMm: f.breedteMm,
529
+ hoogteMm: f.hoogteMm,
530
+ waarde: f.waarde,
531
+ formaat: "CODE128",
532
+ },
533
+ ];
534
+ if (toonWaarde)
535
+ uit.push({
536
+ type: "tekst",
537
+ xMm: bx,
538
+ yMm: y + f.hoogteMm + 0.5,
539
+ breedteMm: f.breedteMm,
540
+ regels: [f.waarde],
541
+ groottePt: grootte,
542
+ vet: false,
543
+ kleur: KLEUR_DONKER,
544
+ uitlijning: "center",
545
+ regelhoogteMm: rh,
546
+ });
547
+ return uit;
548
+ },
549
+ };
550
+ }
551
+ case "afbeelding": {
552
+ const b = Math.min(f.breedteMm ?? f.hoogteMm * 3, breedte);
553
+ const ix = f.uitlijning === "right"
554
+ ? x + breedte - b
555
+ : f.uitlijning === "center"
556
+ ? x + (breedte - b) / 2
557
+ : x;
558
+ return {
559
+ hoogteMm: f.hoogteMm + 1,
560
+ teken: (y) => [
561
+ { type: "afbeelding", xMm: ix, yMm: y, breedteMm: b, hoogteMm: f.hoogteMm, src: f.src },
562
+ ],
563
+ };
564
+ }
565
+ case "lijn": {
566
+ const marge = f.margeMm ?? 1.5;
567
+ return {
568
+ hoogteMm: marge * 2 + (f.dikteMm ?? 0.25),
569
+ teken: (y) => [
570
+ {
571
+ type: "lijn",
572
+ xMm: x,
573
+ yMm: y + marge,
574
+ breedteMm: breedte,
575
+ dikteMm: f.dikteMm ?? 0.25,
576
+ kleur: f.kleur ?? KLEUR_LIJN,
577
+ },
578
+ ],
579
+ };
580
+ }
581
+ case "lijnveld": {
582
+ const grootte = basis;
583
+ const rh = ptNaarMm(regelHoogtePt(grootte));
584
+ const hoogte = f.hoogteMm;
585
+ return {
586
+ hoogteMm: hoogte + 1.5,
587
+ teken: (y) => {
588
+ const uit = [];
589
+ if (f.label)
590
+ uit.push({
591
+ type: "tekst",
592
+ xMm: x,
593
+ yMm: y,
594
+ breedteMm: breedte,
595
+ regels: [f.label],
596
+ groottePt: grootte,
597
+ vet: false,
598
+ kleur: KLEUR_DONKER,
599
+ uitlijning: "left",
600
+ regelhoogteMm: rh,
601
+ });
602
+ uit.push({
603
+ type: "lijn",
604
+ xMm: x,
605
+ yMm: y + hoogte,
606
+ breedteMm: breedte,
607
+ dikteMm: 0.25,
608
+ kleur: KLEUR_GRIJS,
609
+ });
610
+ return uit;
611
+ },
612
+ };
613
+ }
614
+ case "ruimte":
615
+ return { hoogteMm: f.hoogteMm, teken: () => [] };
616
+ case "groep": {
617
+ const b = (breedte * Math.min(Math.max(f.breedtePercent ?? 100, 5), 100)) / 100;
618
+ const padding = f.paddingMm ?? (f.kader ? 1.5 : 0);
619
+ const gx = f.uitlijning === "right" ? x + breedte - b : f.uitlijning === "center" ? x + (breedte - b) / 2 : x;
620
+ const kinderen = f.kinderen
621
+ .filter((k) => k.kind !== "paginabreuk")
622
+ .map((k) => maakBlok(k, gx + padding, b - padding * 2, basis));
623
+ const inhoud = kinderen.reduce((a, k) => a + k.hoogteMm, 0);
624
+ const hoogte = inhoud + padding * 2 + (f.margeOnderMm ?? 0);
625
+ return {
626
+ hoogteMm: hoogte,
627
+ teken: (y) => {
628
+ const uit = [];
629
+ if (f.kader || f.achtergrond)
630
+ uit.push({
631
+ type: "vlak",
632
+ xMm: gx,
633
+ yMm: y,
634
+ breedteMm: b,
635
+ hoogteMm: inhoud + padding * 2,
636
+ vulling: f.achtergrond,
637
+ randKleur: f.kader ? KLEUR_LIJN : undefined,
638
+ randMm: f.kader ? 0.25 : undefined,
639
+ });
640
+ let cy = y + padding;
641
+ for (const k of kinderen) {
642
+ uit.push(...k.teken(cy));
643
+ cy += k.hoogteMm;
644
+ }
645
+ return uit;
646
+ },
647
+ };
648
+ }
649
+ case "tabel": {
650
+ const grootte = f.groottePt ?? basis;
651
+ const kopGrootte = f.kopGroottePt ?? grootte * 0.9;
652
+ const breedtes = kolomBreedtesMm(f.kolommen, breedte);
653
+ const kop = f.toonKop !== false ? kopRijHoogte(f, breedtes, kopGrootte) : null;
654
+ const randen = f.randen ?? "onder";
655
+ const rijHoogtes = f.rijen.map((_, i) => rijHoogte(f, i, breedtes, grootte));
656
+ return {
657
+ hoogteMm: (kop?.hoogte ?? 0) + rijHoogtes.reduce((a, b) => a + b, 0),
658
+ teken: (y) => {
659
+ const uit = [];
660
+ let cy = y;
661
+ if (kop) {
662
+ uit.push(...kop.teken(cy, x, breedtes, randen));
663
+ cy += kop.hoogte;
664
+ }
665
+ f.rijen.forEach((_, i) => {
666
+ uit.push(...tekenRij(f, i, cy, x, breedtes, grootte, randen));
667
+ cy += rijHoogtes[i] ?? 0;
668
+ });
669
+ return uit;
670
+ },
671
+ };
672
+ }
673
+ case "paginabreuk":
674
+ return { hoogteMm: 0, teken: () => [] };
675
+ }
676
+ }
677
+ // --- 5. publieke ingang ------------------------------------------------------
678
+ /**
679
+ * Rekent het inhoudsmodel één keer door tot een bevroren DocumentModel in mm.
680
+ * Dezelfde invoer levert altijd hetzelfde model: geen DOM-metingen, geen
681
+ * browser-afhankelijkheden en geen willekeur.
682
+ */
683
+ export function berekenDocumentModel(flows, opties) {
684
+ const o = {
685
+ breedteMm: opties.breedteMm,
686
+ hoogteMm: opties.hoogteMm,
687
+ margeMm: opties.margeMm,
688
+ basisGroottePt: opties.basisGroottePt ?? 8.5,
689
+ paginanummers: opties.paginanummers ?? false,
690
+ };
691
+ const l = new Layouter(o);
692
+ const voet = opties.paginaVoet ?? [];
693
+ let voetHoogte = 0;
694
+ if (voet.length) {
695
+ voetHoogte = l.meet(voet, l.breedte) + 1.5;
696
+ l.zetVoetHoogte(voetHoogte);
697
+ }
698
+ if (opties.paginaKop?.length)
699
+ l.zetKop(opties.paginaKop);
700
+ l.plaats(flows);
701
+ if (!l.paginas.length)
702
+ l.nieuwePagina();
703
+ if (voet.length) {
704
+ const voetY = o.hoogteMm - o.margeMm - voetHoogte + 1.5;
705
+ for (const pagina of l.paginas) {
706
+ let y = voetY;
707
+ for (const f of voet) {
708
+ const g = maakBlok(f, o.margeMm, o.breedteMm - o.margeMm * 2, o.basisGroottePt);
709
+ pagina.push(...g.teken(y));
710
+ y += g.hoogteMm;
711
+ }
712
+ }
713
+ }
714
+ const model = {
715
+ documentModelVersie: DOCUMENT_MODEL_VERSIE,
716
+ breedteMm: o.breedteMm,
717
+ hoogteMm: o.hoogteMm,
718
+ margeMm: o.margeMm,
719
+ basisGroottePt: o.basisGroottePt,
720
+ paginanummers: o.paginanummers,
721
+ paginas: l.paginas.map((elementen) => ({ elementen })),
722
+ };
723
+ return bevries(model);
724
+ }
725
+ /** Maakt het model definitief; renderers mogen niets meer wijzigen. */
726
+ export function bevries(model) {
727
+ for (const p of model.paginas) {
728
+ for (const e of p.elementen)
729
+ Object.freeze(e);
730
+ Object.freeze(p.elementen);
731
+ Object.freeze(p);
732
+ }
733
+ Object.freeze(model.paginas);
734
+ return Object.freeze(model);
735
+ }