@flowselections/layout-maker 1.36.7 → 1.45.8

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 (123) 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/PaginaDocument.d.ts +4 -1
  14. package/dist-lib/components/PaginaDocument.d.ts.map +1 -1
  15. package/dist-lib/components/PaginaDocument.js +10 -5
  16. package/dist-lib/components/PakbonBuilderPage.d.ts.map +1 -1
  17. package/dist-lib/components/PakbonBuilderPage.js +34 -14
  18. package/dist-lib/components/PakbonPreview.d.ts +4 -2
  19. package/dist-lib/components/PakbonPreview.d.ts.map +1 -1
  20. package/dist-lib/components/PakbonPreview.js +74 -24
  21. package/dist-lib/components/PakbonSectiesPanel.d.ts +5 -3
  22. package/dist-lib/components/PakbonSectiesPanel.d.ts.map +1 -1
  23. package/dist-lib/components/PakbonSectiesPanel.js +8 -5
  24. package/dist-lib/components/PdfMapToestemmingBanner.d.ts +2 -1
  25. package/dist-lib/components/PdfMapToestemmingBanner.d.ts.map +1 -1
  26. package/dist-lib/components/PdfMapToestemmingBanner.js +73 -14
  27. package/dist-lib/components/PdfVoorbeeld.d.ts +20 -0
  28. package/dist-lib/components/PdfVoorbeeld.d.ts.map +1 -0
  29. package/dist-lib/components/PdfVoorbeeld.js +95 -0
  30. package/dist-lib/components/PrintProgrammaUitleg.d.ts.map +1 -1
  31. package/dist-lib/components/PrintProgrammaUitleg.js +1 -1
  32. package/dist-lib/components/PrintQueuePage.d.ts.map +1 -1
  33. package/dist-lib/components/PrintQueuePage.js +81 -43
  34. package/dist-lib/components/PrintplekDetailPage.d.ts.map +1 -1
  35. package/dist-lib/components/PrintplekDetailPage.js +2 -2
  36. package/dist-lib/components/PrintplekWizard.d.ts.map +1 -1
  37. package/dist-lib/components/PrintplekWizard.js +6 -2
  38. package/dist-lib/components/ProductiebonBuilderPage.d.ts.map +1 -1
  39. package/dist-lib/components/ProductiebonBuilderPage.js +13 -7
  40. package/dist-lib/index.d.ts +16 -6
  41. package/dist-lib/index.d.ts.map +1 -1
  42. package/dist-lib/index.js +11 -5
  43. package/dist-lib/integrations/supabase/auth-middleware.d.ts +3585 -1267
  44. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  45. package/dist-lib/integrations/supabase/client.server.d.ts +3585 -1267
  46. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  47. package/dist-lib/integrations/supabase/moduleClient.d.ts +25898 -0
  48. package/dist-lib/integrations/supabase/moduleClient.d.ts.map +1 -0
  49. package/dist-lib/integrations/supabase/moduleClient.js +29 -0
  50. package/dist-lib/integrations/supabase/types.d.ts +3607 -1265
  51. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  52. package/dist-lib/integrations/supabase/types.js +1 -0
  53. package/dist-lib/lib/aiLayout.functions.d.ts +3585 -1267
  54. package/dist-lib/lib/aiLayout.functions.d.ts.map +1 -1
  55. package/dist-lib/lib/code128.d.ts +5 -0
  56. package/dist-lib/lib/code128.d.ts.map +1 -0
  57. package/dist-lib/lib/code128.js +51 -0
  58. package/dist-lib/lib/documentVoorbeeld.functions.d.ts +23 -0
  59. package/dist-lib/lib/documentVoorbeeld.functions.d.ts.map +1 -0
  60. package/dist-lib/lib/documentVoorbeeld.functions.js +37 -0
  61. package/dist-lib/lib/fontMetrics.d.ts +15 -0
  62. package/dist-lib/lib/fontMetrics.d.ts.map +1 -0
  63. package/dist-lib/lib/fontMetrics.js +118 -0
  64. package/dist-lib/lib/htmlNaarPdf.server.d.ts +2 -2
  65. package/dist-lib/lib/htmlNaarPdf.server.d.ts.map +1 -1
  66. package/dist-lib/lib/htmlNaarPdf.server.js +2 -1
  67. package/dist-lib/lib/htmlPdf.server.d.ts +11 -0
  68. package/dist-lib/lib/htmlPdf.server.d.ts.map +1 -1
  69. package/dist-lib/lib/htmlPdf.server.js +170 -9
  70. package/dist-lib/lib/layoutPreview.d.ts.map +1 -1
  71. package/dist-lib/lib/layoutPreview.js +6 -3
  72. package/dist-lib/lib/orderData.server.d.ts.map +1 -1
  73. package/dist-lib/lib/orderData.server.js +49 -16
  74. package/dist-lib/lib/pakbonBarcodeVerzoek.d.ts +39 -0
  75. package/dist-lib/lib/pakbonBarcodeVerzoek.d.ts.map +1 -0
  76. package/dist-lib/lib/pakbonBarcodeVerzoek.js +80 -0
  77. package/dist-lib/lib/pakbonBuilder.d.ts +90 -5
  78. package/dist-lib/lib/pakbonBuilder.d.ts.map +1 -1
  79. package/dist-lib/lib/pakbonBuilder.js +455 -58
  80. package/dist-lib/lib/pakbonContent.d.ts +41 -0
  81. package/dist-lib/lib/pakbonContent.d.ts.map +1 -0
  82. package/dist-lib/lib/pakbonContent.js +668 -0
  83. package/dist-lib/lib/pakbonDocument.d.ts +199 -0
  84. package/dist-lib/lib/pakbonDocument.d.ts.map +1 -0
  85. package/dist-lib/lib/pakbonDocument.js +735 -0
  86. package/dist-lib/lib/pakbonHtml.d.ts +21 -2
  87. package/dist-lib/lib/pakbonHtml.d.ts.map +1 -1
  88. package/dist-lib/lib/pakbonHtml.js +144 -36
  89. package/dist-lib/lib/pakbonPresentation.d.ts +8 -0
  90. package/dist-lib/lib/pakbonPresentation.d.ts.map +1 -0
  91. package/dist-lib/lib/pakbonPresentation.js +31 -0
  92. package/dist-lib/lib/pakbonScan.d.ts +44 -0
  93. package/dist-lib/lib/pakbonScan.d.ts.map +1 -0
  94. package/dist-lib/lib/pakbonScan.js +87 -0
  95. package/dist-lib/lib/pdfJobSaver.d.ts +10 -6
  96. package/dist-lib/lib/pdfJobSaver.d.ts.map +1 -1
  97. package/dist-lib/lib/pdfJobSaver.js +302 -99
  98. package/dist-lib/lib/pdfMap.d.ts +37 -2
  99. package/dist-lib/lib/pdfMap.d.ts.map +1 -1
  100. package/dist-lib/lib/pdfMap.js +120 -7
  101. package/dist-lib/lib/pdfOpslagStatus.d.ts +23 -0
  102. package/dist-lib/lib/pdfOpslagStatus.d.ts.map +1 -0
  103. package/dist-lib/lib/pdfOpslagStatus.js +74 -0
  104. package/dist-lib/lib/printApi.d.ts +31 -0
  105. package/dist-lib/lib/printApi.d.ts.map +1 -1
  106. package/dist-lib/lib/printApi.js +62 -1
  107. package/dist-lib/lib/printQueue.server.d.ts +13 -0
  108. package/dist-lib/lib/printQueue.server.d.ts.map +1 -1
  109. package/dist-lib/lib/printQueue.server.js +673 -96
  110. package/dist-lib/lib/printService.functions.d.ts +11840 -2568
  111. package/dist-lib/lib/printService.functions.d.ts.map +1 -1
  112. package/dist-lib/lib/profielHtml.d.ts.map +1 -1
  113. package/dist-lib/lib/profielHtml.js +8 -1
  114. package/dist-lib/lib/supabaseClient.d.ts +1 -3
  115. package/dist-lib/lib/supabaseClient.d.ts.map +1 -1
  116. package/dist-lib/lib/supabaseClient.js +4 -8
  117. package/dist-lib/lib/useDocumentData.d.ts +8 -0
  118. package/dist-lib/lib/useDocumentData.d.ts.map +1 -1
  119. package/dist-lib/lib/useDocumentData.js +1 -1
  120. package/dist-lib/lib/voorbeeldPrint.d.ts +3 -0
  121. package/dist-lib/lib/voorbeeldPrint.d.ts.map +1 -1
  122. package/dist-lib/lib/voorbeeldPrint.js +65 -17
  123. 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
+ }