@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 @@
1
+ {"version":3,"file":"code128.d.ts","sourceRoot":"","sources":["../../src/lib/code128.ts"],"names":[],"mappings":"AAkBA,8DAA8D;AAC9D,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAqBjD;AAED,wEAAwE;AACxE,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQrD"}
@@ -0,0 +1,51 @@
1
+ // ============================================================================
2
+ // Code 128 subset B — compacte, scanbare barcode voor printdocumenten.
3
+ // ============================================================================
4
+ const PATRONEN = [
5
+ "212222", "222122", "222221", "121223", "121322", "131222", "122213", "122312", "132212", "221213",
6
+ "221312", "231212", "112232", "122132", "122231", "113222", "123122", "123221", "223211", "221132",
7
+ "221231", "213212", "223112", "312131", "311222", "321122", "321221", "312212", "322112", "322211",
8
+ "212123", "212321", "232121", "111323", "131123", "131321", "112313", "132113", "132311", "211313",
9
+ "231113", "231311", "112133", "112331", "132131", "113123", "113321", "133121", "313121", "211331",
10
+ "231131", "213113", "213311", "213131", "311123", "311321", "331121", "312113", "312311", "332111",
11
+ "314111", "221411", "431111", "111224", "111422", "121124", "121421", "141122", "141221", "112214",
12
+ "112412", "122114", "122411", "142112", "142211", "241211", "221114", "413111", "241112", "134111",
13
+ "111242", "121142", "121241", "114212", "124112", "124211", "411212", "421112", "421211", "212141",
14
+ "214121", "412121", "111143", "111341", "131141", "114113", "114311", "411113", "411311", "113141",
15
+ "114131", "311141", "411131", "211412", "211214", "211232", "2331112",
16
+ ];
17
+ /** Geeft de zwart/wit modules van Code 128 subset B terug. */
18
+ export function code128Bars(value) {
19
+ const tekst = String(value ?? "");
20
+ if (!tekst)
21
+ return "";
22
+ const codes = Array.from(tekst, (teken) => {
23
+ const code = teken.charCodeAt(0);
24
+ return code >= 32 && code <= 126 ? code - 32 : "?".charCodeAt(0) - 32;
25
+ });
26
+ const checksum = codes.reduce((som, code, index) => som + code * (index + 1), 104) % 103;
27
+ const symbolen = [104, ...codes, checksum, 106];
28
+ return symbolen
29
+ .map((symbool) => {
30
+ const patroon = PATRONEN[symbool] ?? PATRONEN[0];
31
+ let zwart = true;
32
+ return Array.from(patroon, (breedte) => {
33
+ const modules = Number(breedte);
34
+ const resultaat = (zwart ? "1" : "0").repeat(modules);
35
+ zwart = !zwart;
36
+ return resultaat;
37
+ }).join("");
38
+ })
39
+ .join("");
40
+ }
41
+ /** Browserweergave van de barcode zonder externe barcodebibliotheek. */
42
+ export function code128BarsHtml(value) {
43
+ const bars = code128Bars(value);
44
+ if (!bars)
45
+ return "";
46
+ const modules = bars.length;
47
+ return bars
48
+ .split("")
49
+ .map((bar) => `<span style="display:block;flex:0 0 ${100 / modules}%;height:100%;background:${bar === "1" ? "#111827" : "transparent"}"></span>`)
50
+ .join("");
51
+ }
@@ -0,0 +1,23 @@
1
+ export interface VoorbeeldPdfInvoer {
2
+ html: string;
3
+ breedteMm: number;
4
+ hoogteMm: number;
5
+ margeMm: number;
6
+ paginanummers?: boolean;
7
+ }
8
+ /**
9
+ * Rendert print-HTML met exact dezelfde servercode als de printwachtrij, zodat
10
+ * het voorbeeld in de editor letterlijk de PDF is die straks wordt geprint.
11
+ * Er wordt niets opgeslagen en geen printopdracht aangemaakt.
12
+ */
13
+ export declare const maakVoorbeeldPdf: import("@tanstack/start-client-core").RequiredFetcher<undefined, (invoer: VoorbeeldPdfInvoer) => {
14
+ html: string;
15
+ breedteMm: number;
16
+ hoogteMm: number;
17
+ margeMm: number;
18
+ paginanummers: boolean | undefined;
19
+ }, Promise<{
20
+ pdfBase64: string;
21
+ bytes: number;
22
+ }>>;
23
+ //# sourceMappingURL=documentVoorbeeld.functions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"documentVoorbeeld.functions.d.ts","sourceRoot":"","sources":["../../src/lib/documentVoorbeeld.functions.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,4EACF,kBAAkB;;;;;;;;;GA2BzC,CAAC"}
@@ -0,0 +1,37 @@
1
+ import { createServerFn } from "@tanstack/react-start";
2
+ /**
3
+ * Rendert print-HTML met exact dezelfde servercode als de printwachtrij, zodat
4
+ * het voorbeeld in de editor letterlijk de PDF is die straks wordt geprint.
5
+ * Er wordt niets opgeslagen en geen printopdracht aangemaakt.
6
+ */
7
+ export const maakVoorbeeldPdf = createServerFn({ method: "POST" })
8
+ .inputValidator((invoer) => {
9
+ const html = String(invoer?.html ?? "");
10
+ if (!html.trim())
11
+ throw new Error("Voorbeeld kan niet worden gemaakt: geen HTML aangeleverd.");
12
+ // Pakbonnen met ingesloten logo's/barcodes (data-URI's) worden snel groot;
13
+ // de grens is er alleen om echt onwerkbare invoer te weren.
14
+ if (html.length > 40000000)
15
+ throw new Error("Voorbeeld te groot om te renderen.");
16
+ const breedteMm = Number(invoer?.breedteMm);
17
+ const hoogteMm = Number(invoer?.hoogteMm);
18
+ const margeMm = Number(invoer?.margeMm);
19
+ if (!Number.isFinite(breedteMm) || breedteMm <= 0 || !Number.isFinite(hoogteMm) || hoogteMm <= 0) {
20
+ throw new Error("Voorbeeld kan niet worden gemaakt: papierformaat ontbreekt.");
21
+ }
22
+ if (!Number.isFinite(margeMm) || margeMm < 0 || margeMm * 2 >= Math.min(breedteMm, hoogteMm)) {
23
+ throw new Error("Voorbeeld kan niet worden gemaakt: de ingestelde paginamarge is ongeldig.");
24
+ }
25
+ return { html, breedteMm, hoogteMm, margeMm, paginanummers: invoer?.paginanummers };
26
+ })
27
+ .handler(async ({ data }) => {
28
+ const { htmlNaarPdfBytes, bytesNaarBase64 } = await import("./htmlPdf.server.js");
29
+ const bytes = await htmlNaarPdfBytes({
30
+ html: data.html,
31
+ breedteMm: data.breedteMm,
32
+ hoogteMm: data.hoogteMm,
33
+ margeMm: data.margeMm,
34
+ ...(typeof data.paginanummers === "boolean" ? { paginanummers: data.paginanummers } : {}),
35
+ });
36
+ return { pdfBase64: bytesNaarBase64(bytes), bytes: bytes.length };
37
+ });
@@ -0,0 +1,15 @@
1
+ /** Breedte van tekst in punten bij een gegeven fontgrootte. */
2
+ export declare function tekstBreedtePt(tekst: string, groottePt: number, vet?: boolean): number;
3
+ /** Verhouding regelhoogte / fontgrootte; overal identiek. */
4
+ export declare const REGELHOOGTE_FACTOR = 1.25;
5
+ /** Regelhoogte in punten. */
6
+ export declare function regelHoogtePt(groottePt: number): number;
7
+ /**
8
+ * Breekt tekst af op woordgrenzen binnen een maximale breedte. Woorden die
9
+ * zelf te lang zijn worden hard afgebroken (net als overflow-wrap: anywhere).
10
+ * De uitkomst is de definitieve regelverdeling: renderers wrappen nooit zelf.
11
+ */
12
+ export declare function breekAf(tekst: string, maxBreedtePt: number, groottePt: number, vet?: boolean): string[];
13
+ /** Kort tekst af tot één regel met beletselteken. */
14
+ export declare function kortAf(tekst: string, maxBreedtePt: number, groottePt: number, vet?: boolean): string;
15
+ //# sourceMappingURL=fontMetrics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fontMetrics.d.ts","sourceRoot":"","sources":["../../src/lib/fontMetrics.ts"],"names":[],"mappings":"AA+CA,+DAA+D;AAC/D,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,UAAQ,GAAG,MAAM,CAKpF;AAED,6DAA6D;AAC7D,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAEvC,6BAA6B;AAC7B,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,UAAQ,GAAG,MAAM,EAAE,CA2CrG;AAED,qDAAqD;AACrD,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,UAAQ,GAAG,MAAM,CAMlG"}
@@ -0,0 +1,118 @@
1
+ // ============================================================================
2
+ // src/lib/fontMetrics.ts — centrale fontmetriek voor de pakbon-layoutengine.
3
+ // ----------------------------------------------------------------------------
4
+ // Zowel het live voorbeeld (browser) als de PDF-motor (server) meten tekst met
5
+ // EXACT deze tabellen. Daardoor breekt tekst overal op dezelfde plek af en
6
+ // staan kolommen overal even breed. De waarden zijn de glyph-breedtes van
7
+ // Helvetica / Helvetica-Bold in duizendsten van de fontgrootte (WinAnsi,
8
+ // tekencodes 32 t/m 255) — precies wat de PDF-renderer gebruikt.
9
+ // ============================================================================
10
+ /** Eerste tekencode in de tabellen. */
11
+ const EERSTE = 32;
12
+ const HELVETICA = [
13
+ 278, 278, 355, 556, 556, 889, 667, 191, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556,
14
+ 556, 556, 278, 278, 584, 584, 584, 556, 1015, 667, 667, 722, 722, 667, 611, 778, 722, 278, 500, 667, 556, 833, 722, 778,
15
+ 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 278, 278, 278, 469, 556, 333, 556, 556, 500, 556, 556, 278, 556,
16
+ 556, 222, 222, 500, 222, 833, 556, 556, 556, 556, 333, 500, 278, 556, 500, 722, 500, 500, 500, 334, 260, 334, 584, 0,
17
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
18
+ 0, 0, 0, 0, 0, 0, 0, 0, 278, 333, 556, 556, 556, 556, 260, 556, 333, 737, 370, 556, 584, 333, 737, 333,
19
+ 400, 584, 333, 333, 333, 556, 537, 278, 333, 333, 365, 556, 834, 834, 834, 611, 667, 667, 667, 667, 667, 667, 1000, 722,
20
+ 667, 667, 667, 667, 278, 278, 278, 278, 722, 722, 778, 778, 778, 778, 778, 584, 778, 722, 722, 722, 722, 667, 667, 611,
21
+ 556, 556, 556, 556, 556, 556, 889, 500, 556, 556, 556, 556, 278, 278, 278, 278, 556, 556, 556, 556, 556, 556, 556, 584,
22
+ 611, 556, 556, 556, 556, 500, 556, 500,
23
+ ];
24
+ const HELVETICA_BOLD = [
25
+ 278, 333, 474, 556, 556, 889, 722, 238, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556,
26
+ 556, 556, 333, 333, 584, 584, 584, 611, 975, 722, 722, 722, 722, 667, 611, 778, 722, 278, 556, 722, 611, 833, 722, 778,
27
+ 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 333, 278, 333, 584, 556, 333, 556, 611, 556, 611, 556, 333, 611,
28
+ 611, 278, 278, 556, 278, 889, 611, 611, 611, 611, 389, 556, 333, 611, 556, 778, 556, 556, 500, 389, 280, 389, 584, 0,
29
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
30
+ 0, 0, 0, 0, 0, 0, 0, 0, 278, 333, 556, 556, 556, 556, 280, 556, 333, 737, 370, 556, 584, 333, 737, 333,
31
+ 400, 584, 333, 333, 333, 611, 556, 278, 333, 333, 365, 556, 834, 834, 834, 611, 722, 722, 722, 722, 722, 722, 1000, 722,
32
+ 667, 667, 667, 667, 278, 278, 278, 278, 722, 722, 778, 778, 778, 778, 778, 584, 778, 722, 722, 722, 722, 667, 667, 611,
33
+ 556, 556, 556, 556, 556, 556, 889, 556, 556, 556, 556, 556, 278, 278, 278, 278, 611, 611, 611, 611, 611, 611, 611, 584,
34
+ 611, 611, 611, 611, 611, 556, 611, 556,
35
+ ];
36
+ const ONBEKEND = 556; // breedte van "?" als terugval
37
+ function glyphBreedte(code, vet) {
38
+ const tabel = vet ? HELVETICA_BOLD : HELVETICA;
39
+ const w = tabel[code - EERSTE];
40
+ return typeof w === "number" && w > 0 ? w : ONBEKEND;
41
+ }
42
+ /** Breedte van tekst in punten bij een gegeven fontgrootte. */
43
+ export function tekstBreedtePt(tekst, groottePt, vet = false) {
44
+ let som = 0;
45
+ const s = String(tekst ?? "");
46
+ for (let i = 0; i < s.length; i++)
47
+ som += glyphBreedte(s.charCodeAt(i), vet);
48
+ return (som / 1000) * groottePt;
49
+ }
50
+ /** Verhouding regelhoogte / fontgrootte; overal identiek. */
51
+ export const REGELHOOGTE_FACTOR = 1.25;
52
+ /** Regelhoogte in punten. */
53
+ export function regelHoogtePt(groottePt) {
54
+ return groottePt * REGELHOOGTE_FACTOR;
55
+ }
56
+ /**
57
+ * Breekt tekst af op woordgrenzen binnen een maximale breedte. Woorden die
58
+ * zelf te lang zijn worden hard afgebroken (net als overflow-wrap: anywhere).
59
+ * De uitkomst is de definitieve regelverdeling: renderers wrappen nooit zelf.
60
+ */
61
+ export function breekAf(tekst, maxBreedtePt, groottePt, vet = false) {
62
+ const bron = String(tekst ?? "").replace(/\s+/g, " ").trim();
63
+ if (!bron)
64
+ return [""];
65
+ if (maxBreedtePt <= 0)
66
+ return [bron];
67
+ const regels = [];
68
+ let huidig = "";
69
+ const duwHard = (woord) => {
70
+ let rest = woord;
71
+ while (rest.length) {
72
+ let n = rest.length;
73
+ while (n > 1 && tekstBreedtePt(rest.slice(0, n), groottePt, vet) > maxBreedtePt)
74
+ n--;
75
+ regels.push(rest.slice(0, n));
76
+ rest = rest.slice(n);
77
+ }
78
+ };
79
+ for (const woord of bron.split(" ")) {
80
+ const kandidaat = huidig ? `${huidig} ${woord}` : woord;
81
+ if (tekstBreedtePt(kandidaat, groottePt, vet) <= maxBreedtePt) {
82
+ huidig = kandidaat;
83
+ continue;
84
+ }
85
+ if (huidig) {
86
+ regels.push(huidig);
87
+ huidig = "";
88
+ }
89
+ if (tekstBreedtePt(woord, groottePt, vet) <= maxBreedtePt)
90
+ huidig = woord;
91
+ else {
92
+ const stukken = [];
93
+ let rest = woord;
94
+ while (rest.length) {
95
+ let n = rest.length;
96
+ while (n > 1 && tekstBreedtePt(rest.slice(0, n), groottePt, vet) > maxBreedtePt)
97
+ n--;
98
+ stukken.push(rest.slice(0, n));
99
+ rest = rest.slice(n);
100
+ }
101
+ huidig = stukken.pop() ?? "";
102
+ regels.push(...stukken);
103
+ }
104
+ }
105
+ if (huidig)
106
+ regels.push(huidig);
107
+ return regels.length ? regels : [""];
108
+ }
109
+ /** Kort tekst af tot één regel met beletselteken. */
110
+ export function kortAf(tekst, maxBreedtePt, groottePt, vet = false) {
111
+ const s = String(tekst ?? "");
112
+ if (tekstBreedtePt(s, groottePt, vet) <= maxBreedtePt)
113
+ return s;
114
+ let n = s.length;
115
+ while (n > 1 && tekstBreedtePt(`${s.slice(0, n)}…`, groottePt, vet) > maxBreedtePt)
116
+ n--;
117
+ return `${s.slice(0, n)}…`;
118
+ }
@@ -1,6 +1,6 @@
1
1
  export declare class PdfRenderFout extends Error {
2
- code: "niet_ingesteld" | "mislukt";
3
- constructor(code: "niet_ingesteld" | "mislukt", boodschap: string);
2
+ code: "niet_ingesteld" | "mislukt" | "afwijking";
3
+ constructor(code: "niet_ingesteld" | "mislukt" | "afwijking", boodschap: string);
4
4
  }
5
5
  export interface PdfOpties {
6
6
  html: string;
@@ -1 +1 @@
1
- {"version":3,"file":"htmlNaarPdf.server.d.ts","sourceRoot":"","sources":["../../src/lib/htmlNaarPdf.server.ts"],"names":[],"mappings":"AAQA,qBAAa,aAAc,SAAQ,KAAK;IAE7B,IAAI,EAAE,gBAAgB,GAAG,SAAS;gBAAlC,IAAI,EAAE,gBAAgB,GAAG,SAAS,EACzC,SAAS,EAAE,MAAM;CAKpB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,4DAA4D;AAC5D,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAoB1E"}
1
+ {"version":3,"file":"htmlNaarPdf.server.d.ts","sourceRoot":"","sources":["../../src/lib/htmlNaarPdf.server.ts"],"names":[],"mappings":"AAQA,qBAAa,aAAc,SAAQ,KAAK;IAE7B,IAAI,EAAE,gBAAgB,GAAG,SAAS,GAAG,WAAW;gBAAhD,IAAI,EAAE,gBAAgB,GAAG,SAAS,GAAG,WAAW,EACvD,SAAS,EAAE,MAAM;CAKpB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,4DAA4D;AAC5D,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAqB1E"}
@@ -29,6 +29,7 @@ export async function htmlNaarPdfServer(opties) {
29
29
  return bytesNaarBase64(bytes);
30
30
  }
31
31
  catch (e) {
32
- throw new PdfRenderFout("mislukt", e instanceof Error ? `PDF maken mislukte: ${e.message}` : "PDF maken mislukte.");
32
+ const melding = e instanceof Error ? e.message : "PDF maken mislukte.";
33
+ throw new PdfRenderFout(melding.startsWith("Afdrukafwijking gevonden:") ? "afwijking" : "mislukt", melding.startsWith("Afdrukafwijking gevonden:") ? melding : `PDF maken mislukte: ${melding}`);
33
34
  }
34
35
  }
@@ -41,6 +41,17 @@ export interface DocumentPdfOpties {
41
41
  /** Voettekst met paginanummers ("Pagina 1 van 3"). */
42
42
  paginanummers?: boolean;
43
43
  }
44
+ export interface PdfRenderDiagnostiek {
45
+ geldig: boolean;
46
+ fouten: string[];
47
+ }
48
+ /**
49
+ * Bewaakt constructies die de eigen PDF-motor niet hetzelfde kan uitleggen als
50
+ * een browser. @page is toegestaan: papier en marge komen uit de canonieke
51
+ * DocumentPdfOpties. Andere at-rules en complexe selectors worden geweigerd in
52
+ * plaats van stilletjes genegeerd.
53
+ */
54
+ export declare function controleerPdfOpmaak(html: string): PdfRenderDiagnostiek;
44
55
  /** Zet print-HTML om naar een PDF en levert die als base64 (zonder prefix). */
45
56
  export declare function htmlNaarPdfBytes(opties: DocumentPdfOpties): Promise<Uint8Array>;
46
57
  export declare function bytesNaarBase64(bytes: Uint8Array): string;
@@ -1 +1 @@
1
- {"version":3,"file":"htmlPdf.server.d.ts","sourceRoot":"","sources":["../../src/lib/htmlPdf.server.ts"],"names":[],"mappings":"AAWA,OAAO,EAOL,KAAK,GAAG,EACT,MAAM,SAAS,CAAC;AAEjB,eAAO,MAAM,EAAE,oBAAoB,CAAC;AAIpC,MAAM,WAAW,KAAK;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,QAAQ,EAAE,KAAK,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAuBD,kEAAkE;AAClE,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,CAuC/C;AAUD,UAAU,SAAS;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AACD,UAAU,QAAQ;IAChB,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AAED,sEAAsE;AACtE,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMpD;AA2BD,yFAAyF;AACzF,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,EAAE,CAgClD;AAqBD,6EAA6E;AAC7E,wBAAgB,UAAU,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAiBlE;AAKD,MAAM,WAAW,KAAK;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,OAAO,CAAC;IACb,KAAK,EAAE,GAAG,CAAC;IACX,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;IACxC,mDAAmD;IACnD,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAmjCD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,sDAAsD;IACtD,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,+EAA+E;AAC/E,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,UAAU,CAAC,CA+DrF;AA0BD,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAQzD"}
1
+ {"version":3,"file":"htmlPdf.server.d.ts","sourceRoot":"","sources":["../../src/lib/htmlPdf.server.ts"],"names":[],"mappings":"AAYA,OAAO,EAOL,KAAK,GAAG,EACT,MAAM,SAAS,CAAC;AAEjB,eAAO,MAAM,EAAE,oBAAoB,CAAC;AAIpC,MAAM,WAAW,KAAK;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,QAAQ,EAAE,KAAK,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAuBD,kEAAkE;AAClE,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,CAuC/C;AAUD,UAAU,SAAS;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AACD,UAAU,QAAQ;IAChB,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AAED,sEAAsE;AACtE,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMpD;AA2BD,yFAAyF;AACzF,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,EAAE,CAgClD;AAqBD,6EAA6E;AAC7E,wBAAgB,UAAU,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAiBlE;AAKD,MAAM,WAAW,KAAK;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,OAAO,CAAC;IACb,KAAK,EAAE,GAAG,CAAC;IACX,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;IACxC,mDAAmD;IACnD,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAypCD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,sDAAsD;IACtD,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,CA8BtE;AAED,+EAA+E;AAC/E,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,UAAU,CAAC,CA6ErF;AA0BD,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAQzD"}
@@ -8,6 +8,7 @@
8
8
  // rijen naast elkaar, tabellen met vaste kolombreedtes, kaders, lijnen,
9
9
  // afbeeldingen, barcodes en QR-vlakken.
10
10
  // ============================================================================
11
+ import { code128Bars } from "./code128.js";
11
12
  import { PDFDocument, StandardFonts, rgb, } from "pdf-lib";
12
13
  export const MM = 2.834645669291339; // punten per millimeter
13
14
  const LEEG = new Set(["img", "br", "hr", "col", "meta", "link", "input"]);
@@ -524,6 +525,16 @@ async function meetHoogte(ctx, n, vak, s) {
524
525
  await tekenKinderen(meetCtx, { tag: "div", attrs: {}, kinderen: [n] }, vak, s);
525
526
  });
526
527
  }
528
+ /**
529
+ * Meet uitsluitend de inhoud van een blok. Het blok zelf meemeten telt padding
530
+ * en marges dubbel; dan valt een achtergrondvlak zichtbaar te ver door.
531
+ */
532
+ async function meetInhoud(ctx, n, vak, s) {
533
+ const meetCtx = { ...ctx, meten: true };
534
+ return ctx.tk.meet(async () => {
535
+ await tekenKinderen(meetCtx, n, vak, s);
536
+ });
537
+ }
527
538
  function rijenVan(tabel) {
528
539
  const rijen = [];
529
540
  const loop = (k) => {
@@ -561,6 +572,25 @@ async function tekenBlok(ctx, n, vak, ouderStijl) {
561
572
  const s = stijlVan(n, ouderStijl);
562
573
  const st = eig(n);
563
574
  const cl = klassen(n);
575
+ const barcode = eigenBarcodeWaarde(n);
576
+ if (barcode) {
577
+ const balkNode = zoekDirecteBarcodeNode(n);
578
+ const h = lengte(balkNode ? eig(balkNode)["height"] : st["height"]) ?? 15 * MM;
579
+ const onderschrift = heeft(n, "barcode") ? plattTekst(n).trim() : "";
580
+ const onderschriftStijl = { ...s, grootte: Math.min(s.grootte, 7), uitlijning: "center" };
581
+ const tekstHoogte = onderschrift ? regelHoogte(onderschriftStijl) : 0;
582
+ const barcodeBreedte = Math.min(45 * MM, vak.breedte);
583
+ const barcodeX = vak.x + Math.max(0, (vak.breedte - barcodeBreedte) / 2);
584
+ tk.zorgVoor(h + tekstHoogte + 1 * MM);
585
+ tekenBarcode(tk, barcode, barcodeX, barcodeBreedte, h);
586
+ tk.y -= h;
587
+ if (onderschrift) {
588
+ tk.y -= tekstHoogte;
589
+ tekenRegel(tk, onderschrift, vak.x, vak.breedte, tk.y + onderschriftStijl.grootte * 0.25, onderschriftStijl);
590
+ }
591
+ tk.y -= 1 * MM;
592
+ return;
593
+ }
564
594
  if (n.tag === "#tekst") {
565
595
  const regels = breekAf(tk, n.tekst ?? "", s, vak.breedte);
566
596
  for (const r of regels) {
@@ -686,7 +716,7 @@ async function tekenBlok(ctx, n, vak, ouderStijl) {
686
716
  const heelHouden = cl.includes("heelhouden") || /avoid/.test(st["break-inside"] ?? st["page-break-inside"] ?? "");
687
717
  const binnenVak = { x: vak.x + padding, breedte: vak.breedte - padding * 2 };
688
718
  if (!ctx.meten && (heelHouden || achtergrond)) {
689
- const nodig = (await meetHoogte(ctx, n, binnenVak, s)) + padding * 2;
719
+ const nodig = (await meetInhoud(ctx, n, binnenVak, s)) + padding * 2;
690
720
  if (heelHouden && nodig > tk.ruimte && nodig < tk.hoogte - tk.marge * 2) {
691
721
  tk.nieuwePagina();
692
722
  }
@@ -807,7 +837,18 @@ async function tekenTabel(ctx, tabel, vak, s) {
807
837
  const cellen = rij.kinderen.filter((c) => c.tag === "td" || c.tag === "th");
808
838
  const celBreedte = (i) => Math.max(4, (breedtes[i] ?? 20) - padX * 2);
809
839
  const celTekst = cellen.map((c) => plattTekst(c));
810
- const cellenStijl = cellen.map((c, i) => passendeStijl(tk, celTekst[i], stijlVan(c, s), celBreedte(i)));
840
+ // Eén verkleiningsfactor voor de hele rij: losse cellen die elk hun eigen
841
+ // maat kiezen leveren een rommelige, ongelijke kop- en gegevensregel op.
842
+ const ruwStijl = cellen.map((c) => stijlVan(c, s));
843
+ const factor = cellen.reduce((laagste, _c, i) => {
844
+ const basis = ruwStijl[i];
845
+ const passend = passendeStijl(tk, celTekst[i], basis, celBreedte(i));
846
+ return Math.min(laagste, passend.grootte / basis.grootte);
847
+ }, 1);
848
+ const cellenStijl = ruwStijl.map((st) => ({
849
+ ...st,
850
+ grootte: Math.round(st.grootte * factor * 10) / 10,
851
+ }));
811
852
  const teksten = cellen.map((_, i) => breekAf(tk, celTekst[i], cellenStijl[i], celBreedte(i)));
812
853
  const hoogte = Math.max(regelHoogte(s), ...teksten.map((t, i) => Math.max(1, t.length) * regelHoogte(cellenStijl[i] ?? s))) + padY * 2;
813
854
  if (hoogte > tk.ruimte) {
@@ -886,6 +927,17 @@ async function tekenAbsoluut(ctx, n, s) {
886
927
  const hoogte = lengte(st["height"]) ?? regelHoogte(eigen);
887
928
  const yBoven = tk.hoogte - top;
888
929
  const yOnder = yBoven - hoogte;
930
+ const barcode = eigenBarcodeWaarde(n);
931
+ if (barcode) {
932
+ const barcodeHoogte = Math.max(5 * MM, hoogte * 0.72);
933
+ tekenBarcode(tk, barcode, x, breedte, barcodeHoogte, yBoven);
934
+ const onderschrift = plattTekst(n).trim();
935
+ if (onderschrift) {
936
+ const kleinStijl = { ...eigen, grootte: Math.min(eigen.grootte, 7), uitlijning: "center" };
937
+ tekenRegel(tk, onderschrift, x, breedte, yOnder + 1, kleinStijl);
938
+ }
939
+ return;
940
+ }
889
941
  const gradientKind = zoekGradient(n);
890
942
  if (gradientKind) {
891
943
  const gebied = gradientKind === "streepjes" ? hoogte * 0.72 : Math.min(breedte, hoogte);
@@ -1005,6 +1057,62 @@ async function tekenAbsoluut(ctx, n, s) {
1005
1057
  tekenRegel(tk, r, x + padding, breedte - padding * 2, y + eigen.grootte * 0.25, eigen);
1006
1058
  }
1007
1059
  }
1060
+ /** Tekent de waarde-encoded Code 128 subset B barcode. */
1061
+ function tekenBarcode(tk, waarde, x, breedte, hoogte, yBoven) {
1062
+ const patroon = code128Bars(waarde);
1063
+ if (!patroon)
1064
+ return;
1065
+ const boven = yBoven ?? tk.y;
1066
+ const y = boven - hoogte;
1067
+ let start = 0;
1068
+ let zwart = patroon[0] === "1";
1069
+ for (let i = 1; i <= patroon.length; i += 1) {
1070
+ const huidig = patroon[i] === "1";
1071
+ if (i === patroon.length || huidig !== zwart) {
1072
+ if (zwart) {
1073
+ tk.pagina.drawRectangle({
1074
+ x: x + (start / patroon.length) * breedte,
1075
+ y,
1076
+ width: ((i - start) / patroon.length) * breedte,
1077
+ height: hoogte,
1078
+ color: DONKER,
1079
+ });
1080
+ }
1081
+ start = i;
1082
+ zwart = huidig;
1083
+ }
1084
+ }
1085
+ }
1086
+ /** Leest alleen expliciet door de renderer aangeleverde barcodewaarden. */
1087
+ function zoekBarcodeWaarde(n) {
1088
+ const waarde = String(n.attrs["data-barcode"] ?? "").trim();
1089
+ if (waarde)
1090
+ return waarde;
1091
+ for (const k of n.kinderen) {
1092
+ const diep = zoekBarcodeWaarde(k);
1093
+ if (diep)
1094
+ return diep;
1095
+ }
1096
+ return null;
1097
+ }
1098
+ /** Barcode alleen wanneer dit element zelf de barcode of barcodecontainer is. */
1099
+ function eigenBarcodeWaarde(n) {
1100
+ const eigen = String(n.attrs["data-barcode"] ?? "").trim();
1101
+ if (eigen)
1102
+ return eigen;
1103
+ if (!heeft(n, "barcode"))
1104
+ return null;
1105
+ return zoekBarcodeWaarde(n);
1106
+ }
1107
+ function zoekDirecteBarcodeNode(n) {
1108
+ if (String(n.attrs["data-barcode"] ?? "").trim())
1109
+ return n;
1110
+ for (const kind of n.kinderen) {
1111
+ if (String(kind.attrs["data-barcode"] ?? "").trim())
1112
+ return kind;
1113
+ }
1114
+ return null;
1115
+ }
1008
1116
  /** Zoekt een streepjes- of blokjespatroon (barcode/QR) in de knoop of kinderen. */
1009
1117
  function zoekGradient(n) {
1010
1118
  const css = `${eig(n)["background"] ?? ""} ${eig(n)["background-image"] ?? ""}`;
@@ -1049,8 +1157,6 @@ async function tekenKinderen(ctx, n, vak, s) {
1049
1157
  (/grid/.test(display) && kolommen.trim().split(/\s+/).length === 2);
1050
1158
  const rijModus = /flex/.test(display) ||
1051
1159
  cl.includes("kv") ||
1052
- cl.includes("karkop") ||
1053
- cl.includes("laagkop") ||
1054
1160
  cl.includes("meta") ||
1055
1161
  cl.includes("totalen") ||
1056
1162
  cl.includes("regelrij");
@@ -1088,8 +1194,7 @@ async function tekenKinderen(ctx, n, vak, s) {
1088
1194
  const spreiden = stukken.length > 1 &&
1089
1195
  (verdeel === "space-between" ||
1090
1196
  verdeel === "space-around" ||
1091
- ((!verdeel || verdeel === "normal") &&
1092
- (cl.includes("kv") || cl.includes("karkop") || cl.includes("laagkop"))));
1197
+ ((!verdeel || verdeel === "normal") && cl.includes("kv")));
1093
1198
  const rechtsUit = verdeel === "flex-end" || verdeel === "right" || verdeel === "end";
1094
1199
  if (spreiden) {
1095
1200
  let x = vak.x;
@@ -1195,11 +1300,64 @@ async function laadAfbeeldingen(pdf, wortel) {
1195
1300
  }));
1196
1301
  return { beelden, fouten };
1197
1302
  }
1303
+ /**
1304
+ * Bewaakt constructies die de eigen PDF-motor niet hetzelfde kan uitleggen als
1305
+ * een browser. @page is toegestaan: papier en marge komen uit de canonieke
1306
+ * DocumentPdfOpties. Andere at-rules en complexe selectors worden geweigerd in
1307
+ * plaats van stilletjes genegeerd.
1308
+ */
1309
+ export function controleerPdfOpmaak(html) {
1310
+ const css = leesStylesheets(html).replace(/\/\*[\s\S]*?\*\//g, "");
1311
+ const fouten = new Set();
1312
+ for (const match of css.matchAll(/@([\w-]+)/g)) {
1313
+ const naam = String(match[1] ?? "").toLowerCase();
1314
+ if (naam !== "page")
1315
+ fouten.add(`CSS-regel @${naam} wordt niet ondersteund door de afdrukmotor.`);
1316
+ }
1317
+ let i = 0;
1318
+ while (i < css.length) {
1319
+ const open = css.indexOf("{", i);
1320
+ if (open < 0)
1321
+ break;
1322
+ const kop = css.slice(i, open).trim();
1323
+ let diepte = 0;
1324
+ let einde = open;
1325
+ for (; einde < css.length; einde += 1) {
1326
+ if (css[einde] === "{")
1327
+ diepte += 1;
1328
+ else if (css[einde] === "}") {
1329
+ diepte -= 1;
1330
+ if (!diepte)
1331
+ break;
1332
+ }
1333
+ }
1334
+ i = einde + 1;
1335
+ if (!kop || kop.startsWith("@"))
1336
+ continue;
1337
+ for (const selector of kop.split(",")) {
1338
+ if (!leesSelector(selector)) {
1339
+ fouten.add(`CSS-selector '${selector.trim()}' wordt niet ondersteund door de afdrukmotor.`);
1340
+ }
1341
+ }
1342
+ }
1343
+ return { geldig: fouten.size === 0, fouten: [...fouten] };
1344
+ }
1198
1345
  /** Zet print-HTML om naar een PDF en levert die als base64 (zonder prefix). */
1199
1346
  export async function htmlNaarPdfBytes(opties) {
1200
1347
  const bMm = Number(opties.breedteMm ?? 210);
1201
1348
  const hMm = Number(opties.hoogteMm ?? 297);
1202
- const marge = Number(opties.margeMm ?? 10) * MM;
1349
+ const margeMm = Number(opties.margeMm ?? 10);
1350
+ if (!Number.isFinite(bMm) || !Number.isFinite(hMm) || bMm <= 0 || hMm <= 0) {
1351
+ throw new Error("Het papierformaat is ongeldig.");
1352
+ }
1353
+ if (!Number.isFinite(margeMm) || margeMm < 0 || margeMm * 2 >= Math.min(bMm, hMm)) {
1354
+ throw new Error("De paginamarge is ongeldig.");
1355
+ }
1356
+ const diagnostiek = controleerPdfOpmaak(opties.html);
1357
+ if (!diagnostiek.geldig) {
1358
+ throw new Error(`Afdrukafwijking gevonden: ${diagnostiek.fouten.join(" ")}`);
1359
+ }
1360
+ const marge = margeMm * MM;
1203
1361
  const breedte = (opties.landschap ? Math.max(bMm, hMm) : Math.min(bMm, hMm)) * MM;
1204
1362
  const hoogte = (opties.landschap ? Math.min(bMm, hMm) : Math.max(bMm, hMm)) * MM;
1205
1363
  const pdf = await PDFDocument.create();
@@ -1210,6 +1368,9 @@ export async function htmlNaarPdfBytes(opties) {
1210
1368
  // het document er hetzelfde uitziet als in het voorbeeld op het scherm.
1211
1369
  stempelCss(wortel, ontleedCss(leesStylesheets(opties.html)));
1212
1370
  const { beelden: afbeeldingen, fouten: beeldFouten } = await laadAfbeeldingen(pdf, wortel);
1371
+ if (beeldFouten.size) {
1372
+ throw new Error(`Afdrukafwijking gevonden: ${[...beeldFouten.values()].join(" ")}`);
1373
+ }
1213
1374
  const basis = basisGrootte(opties.html);
1214
1375
  const tk = new Tekenaar(pdf, breedte, hoogte, marge, normaal, vet);
1215
1376
  const ctx = { tk, afbeeldingen, beeldFouten };
@@ -1234,8 +1395,8 @@ export async function htmlNaarPdfBytes(opties) {
1234
1395
  }
1235
1396
  // De layout bepaalt of paginanummers meegaan; zonder voorkeur alleen bij
1236
1397
  // meerdere pagina's.
1237
- const uitMeta = /name=["']pdf-paginanummers["']\s+content=["']ja["']/i.test(opties.html);
1238
- const nummers = opties.paginanummers ?? (uitMeta ? true : tk.paginas.length > 1);
1398
+ const meta = /name=["']pdf-paginanummers["']\s+content=["'](ja|nee)["']/i.exec(opties.html)?.[1];
1399
+ const nummers = opties.paginanummers ?? (meta ? meta === "ja" : tk.paginas.length > 1);
1239
1400
  if (nummers) {
1240
1401
  tk.paginas.forEach((p, i) => {
1241
1402
  const t = `Pagina ${i + 1} van ${tk.paginas.length}`;
@@ -1 +1 @@
1
- {"version":3,"file":"layoutPreview.d.ts","sourceRoot":"","sources":["../../src/lib/layoutPreview.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEvE,wBAAgB,gBAAgB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAIzD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAE5E;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,kBAAkB,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAuB5F;AAED,eAAO,MAAM,gBAAgB;;;;;;;;;;;GAI5B,CAAC;AA0FF,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,WAAW,EACnB,SAAS,EAAE,kBAAkB,EAAE,EAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAChC,MAAM,CAaR"}
1
+ {"version":3,"file":"layoutPreview.d.ts","sourceRoot":"","sources":["../../src/lib/layoutPreview.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEvE,wBAAgB,gBAAgB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAIzD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAE5E;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,kBAAkB,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAuB5F;AAED,eAAO,MAAM,gBAAgB;;;;;;;;;;;GAI5B,CAAC;AA4FF,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,WAAW,EACnB,SAAS,EAAE,kBAAkB,EAAE,EAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAChC,MAAM,CAaR"}
@@ -2,6 +2,7 @@
2
2
  // src/lib/layoutPreview.ts — voorbeeldgegevens, veldvervanging en print-HTML
3
3
  // ============================================================================
4
4
  import { alleVelden } from "./fieldRegistry.js";
5
+ import { code128BarsHtml } from "./code128.js";
5
6
  export function voorbeeldContext() {
6
7
  const ctx = {};
7
8
  for (const v of alleVelden())
@@ -79,9 +80,11 @@ function elementHtml(el, ctx) {
79
80
  case "cirkel":
80
81
  return `<div style="${base};border-radius:50%;border:${s.randbreedte || 0.3}mm solid ${s.randkleur ?? "#111827"}"></div>`;
81
82
  case "barcode":
82
- return `<div style="${base};display:flex;flex-direction:column;justify-content:center;align-items:center;font-family:monospace">
83
- <div style="width:100%;height:70%;background:repeating-linear-gradient(90deg,#111 0 1.2px,#fff 1.2px 3px)"></div>
84
- <div style="font-size:7pt">${esc(inhoud)}</div></div>`;
83
+ return inhoud
84
+ ? `<div style="${base};display:flex;flex-direction:column;justify-content:center;align-items:center;font-family:monospace">
85
+ <div class="barcode-bars" data-barcode="${esc(inhoud)}" style="width:100%;height:70%;display:flex;background:#fff">${code128BarsHtml(inhoud)}</div>
86
+ <div style="font-size:7pt">${esc(inhoud)}</div></div>`
87
+ : "";
85
88
  case "qrcode":
86
89
  return `<div style="${base};background:repeating-conic-gradient(#111 0% 25%, #fff 0% 50%) 0 0/22% 22%"></div>`;
87
90
  case "logo":
@@ -1 +1 @@
1
- {"version":3,"file":"orderData.server.d.ts","sourceRoot":"","sources":["../../src/lib/orderData.server.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAEnD,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAE/B,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;AAwBtD,mEAAmE;AACnE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAKvD;AAED,+EAA+E;AAC/E,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG;IACrD,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAgCA;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG;IAC7C,KAAK,EAAE,WAAW,CAAC;IACnB,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAMA;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,EAAE,EAAE,OAAO,EACX,MAAM,EAAE;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAC7E,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAwXrB;AA+DD;;;GAGG;AACH,wBAAsB,uBAAuB,CAAC,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAGhG"}
1
+ {"version":3,"file":"orderData.server.d.ts","sourceRoot":"","sources":["../../src/lib/orderData.server.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAEnD,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAE/B,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;AAwBtD,mEAAmE;AACnE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAKvD;AAED,+EAA+E;AAC/E,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG;IACrD,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAgCA;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG;IAC7C,KAAK,EAAE,WAAW,CAAC;IACnB,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAMA;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,EAAE,EAAE,OAAO,EACX,MAAM,EAAE;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAC7E,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CA8ZrB;AA+DD;;;GAGG;AACH,wBAAsB,uBAAuB,CAAC,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAGhG"}