@flowselections/layout-maker 1.38.4 → 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.
- package/README.md +139 -139
- package/dist-lib/components/AfdrukKnop.d.ts +19 -19
- package/dist-lib/components/AfleverbriefBuilderPage.d.ts +10 -0
- package/dist-lib/components/AfleverbriefBuilderPage.d.ts.map +1 -0
- package/dist-lib/components/AfleverbriefBuilderPage.js +101 -0
- package/dist-lib/components/BlokOntwerperPage.d.ts.map +1 -1
- package/dist-lib/components/BlokOntwerperPage.js +8 -3
- package/dist-lib/components/ComputersLijst.d.ts.map +1 -1
- package/dist-lib/components/ComputersLijst.js +1 -1
- package/dist-lib/components/FustKolomVolgorde.d.ts +11 -0
- package/dist-lib/components/FustKolomVolgorde.d.ts.map +1 -0
- package/dist-lib/components/FustKolomVolgorde.js +52 -0
- package/dist-lib/components/LayoutsPage.d.ts.map +1 -1
- package/dist-lib/components/LayoutsPage.js +10 -9
- package/dist-lib/components/NetwerkPrintersDialog.d.ts +7 -7
- package/dist-lib/components/PakbonBuilderPage.d.ts.map +1 -1
- package/dist-lib/components/PakbonBuilderPage.js +32 -14
- package/dist-lib/components/PakbonPreview.d.ts.map +1 -1
- package/dist-lib/components/PakbonPreview.js +21 -14
- package/dist-lib/components/PakbonSectiesPanel.d.ts +4 -2
- package/dist-lib/components/PakbonSectiesPanel.d.ts.map +1 -1
- package/dist-lib/components/PakbonSectiesPanel.js +5 -3
- package/dist-lib/components/PdfMapToestemmingBanner.d.ts +2 -1
- package/dist-lib/components/PdfMapToestemmingBanner.d.ts.map +1 -1
- package/dist-lib/components/PdfMapToestemmingBanner.js +73 -14
- package/dist-lib/components/PdfVoorbeeld.d.ts +20 -0
- package/dist-lib/components/PdfVoorbeeld.d.ts.map +1 -0
- package/dist-lib/components/PdfVoorbeeld.js +95 -0
- package/dist-lib/components/PrintProgrammaUitleg.d.ts.map +1 -1
- package/dist-lib/components/PrintProgrammaUitleg.js +1 -1
- package/dist-lib/components/PrintQueuePage.d.ts.map +1 -1
- package/dist-lib/components/PrintQueuePage.js +81 -43
- package/dist-lib/components/PrinterSetupWizard.js +3 -3
- package/dist-lib/components/PrintersRedirectPage.d.ts +1 -1
- package/dist-lib/components/PrintersRedirectPage.js +14 -14
- package/dist-lib/components/PrintplekDetailPage.d.ts.map +1 -1
- package/dist-lib/components/PrintplekDetailPage.js +2 -2
- package/dist-lib/components/PrintplekWizard.d.ts.map +1 -1
- package/dist-lib/components/PrintplekWizard.js +6 -2
- package/dist-lib/components/ProductiebonBuilderPage.d.ts.map +1 -1
- package/dist-lib/components/ProductiebonBuilderPage.js +13 -7
- package/dist-lib/index.d.ts +13 -5
- package/dist-lib/index.d.ts.map +1 -1
- package/dist-lib/index.js +10 -5
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +2008 -118
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +2008 -118
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.js +1 -1
- package/dist-lib/integrations/supabase/moduleClient.d.ts +2008 -118
- package/dist-lib/integrations/supabase/moduleClient.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +2031 -99
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.js +1 -0
- package/dist-lib/lib/aiLayout.functions.d.ts +2008 -118
- package/dist-lib/lib/aiLayout.functions.d.ts.map +1 -1
- package/dist-lib/lib/aiLayout.server.js +9 -9
- package/dist-lib/lib/browserPrint.js +1 -1
- package/dist-lib/lib/documentVoorbeeld.functions.d.ts +23 -0
- package/dist-lib/lib/documentVoorbeeld.functions.d.ts.map +1 -0
- package/dist-lib/lib/documentVoorbeeld.functions.js +37 -0
- package/dist-lib/lib/fontMetrics.d.ts +15 -0
- package/dist-lib/lib/fontMetrics.d.ts.map +1 -0
- package/dist-lib/lib/fontMetrics.js +118 -0
- package/dist-lib/lib/htmlNaarPdf.server.d.ts +2 -2
- package/dist-lib/lib/htmlNaarPdf.server.d.ts.map +1 -1
- package/dist-lib/lib/htmlNaarPdf.server.js +2 -1
- package/dist-lib/lib/htmlPdf.server.d.ts +11 -0
- package/dist-lib/lib/htmlPdf.server.d.ts.map +1 -1
- package/dist-lib/lib/htmlPdf.server.js +118 -15
- package/dist-lib/lib/layoutPreview.js +7 -7
- package/dist-lib/lib/orderData.server.d.ts.map +1 -1
- package/dist-lib/lib/orderData.server.js +43 -16
- package/dist-lib/lib/pakbonBarcodeVerzoek.d.ts +39 -0
- package/dist-lib/lib/pakbonBarcodeVerzoek.d.ts.map +1 -0
- package/dist-lib/lib/pakbonBarcodeVerzoek.js +80 -0
- package/dist-lib/lib/pakbonBuilder.d.ts +62 -3
- package/dist-lib/lib/pakbonBuilder.d.ts.map +1 -1
- package/dist-lib/lib/pakbonBuilder.js +386 -50
- package/dist-lib/lib/pakbonContent.d.ts +41 -0
- package/dist-lib/lib/pakbonContent.d.ts.map +1 -0
- package/dist-lib/lib/pakbonContent.js +668 -0
- package/dist-lib/lib/pakbonDocument.d.ts +199 -0
- package/dist-lib/lib/pakbonDocument.d.ts.map +1 -0
- package/dist-lib/lib/pakbonDocument.js +735 -0
- package/dist-lib/lib/pakbonHtml.d.ts +7 -0
- package/dist-lib/lib/pakbonHtml.d.ts.map +1 -1
- package/dist-lib/lib/pakbonHtml.js +142 -126
- package/dist-lib/lib/pakbonPresentation.d.ts +8 -0
- package/dist-lib/lib/pakbonPresentation.d.ts.map +1 -0
- package/dist-lib/lib/pakbonPresentation.js +31 -0
- package/dist-lib/lib/pakbonScan.d.ts.map +1 -1
- package/dist-lib/lib/pakbonScan.js +20 -38
- package/dist-lib/lib/pdfJobSaver.d.ts +10 -6
- package/dist-lib/lib/pdfJobSaver.d.ts.map +1 -1
- package/dist-lib/lib/pdfJobSaver.js +302 -99
- package/dist-lib/lib/pdfMap.d.ts +37 -2
- package/dist-lib/lib/pdfMap.d.ts.map +1 -1
- package/dist-lib/lib/pdfMap.js +120 -7
- package/dist-lib/lib/pdfOpslagStatus.d.ts +23 -0
- package/dist-lib/lib/pdfOpslagStatus.d.ts.map +1 -0
- package/dist-lib/lib/pdfOpslagStatus.js +74 -0
- package/dist-lib/lib/printApi.d.ts +31 -0
- package/dist-lib/lib/printApi.d.ts.map +1 -1
- package/dist-lib/lib/printApi.js +62 -1
- package/dist-lib/lib/printQueue.server.d.ts +6 -0
- package/dist-lib/lib/printQueue.server.d.ts.map +1 -1
- package/dist-lib/lib/printQueue.server.js +450 -94
- package/dist-lib/lib/printService.functions.d.ts +7916 -356
- package/dist-lib/lib/printService.functions.d.ts.map +1 -1
- package/dist-lib/lib/productiebonHtml.js +15 -15
- package/dist-lib/lib/profielHtml.d.ts.map +1 -1
- package/dist-lib/lib/profielHtml.js +26 -19
- package/dist-lib/lib/supabaseAdmin.server.d.ts +5 -5
- package/dist-lib/lib/supabaseAdmin.server.js +43 -43
- package/dist-lib/lib/testpagina.js +3 -3
- package/dist-lib/lib/testprint.js +4 -4
- package/dist-lib/lib/useDocumentData.d.ts +8 -0
- package/dist-lib/lib/useDocumentData.d.ts.map +1 -1
- package/dist-lib/lib/useDocumentData.js +1 -1
- package/dist-lib/lib/voorbeeldPrint.d.ts +3 -0
- package/dist-lib/lib/voorbeeldPrint.d.ts.map +1 -1
- package/dist-lib/lib/voorbeeldPrint.js +65 -17
- package/package.json +158 -151
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
// src/lib/aiLayout.server.ts — hulpfuncties voor de AI-layoutgenerator
|
|
3
3
|
// ============================================================================
|
|
4
4
|
const GATEWAY = "https://ai.gateway.lovable.dev/v1/chat/completions";
|
|
5
|
-
const SYSTEM_PROMPT = `Je bent een documentontwerper voor een Nederlands ERP-systeem.
|
|
6
|
-
Je analyseert een bestaand document (PDF of afbeelding) en zet het om naar een herbruikbare layout.
|
|
7
|
-
Herken logo's, adresblokken, koppen, tabellen met kolommen, totalen, barcodes en voetteksten.
|
|
8
|
-
Vervang concrete gegevens door veldcodes zoals {{order.nummer}}, {{klant.naam}}, {{klant.adres}},
|
|
9
|
-
{{factuur.totaal}}, {{systeem.datum}}. Coördinaten en afmetingen zijn in millimeters, linksboven is 0,0.
|
|
10
|
-
Herken logo's als element van het type "logo". Je krijgt een lijst met logo's die al in de centrale
|
|
11
|
-
Logo Bibliotheek staan; als het herkende logo daar duidelijk bij past, zet dan die exacte naam in
|
|
12
|
-
"logo_naam". Past het bij geen enkel bekend logo, laat "logo_naam" dan leeg zodat het als nieuw logo
|
|
13
|
-
kan worden voorgesteld.
|
|
5
|
+
const SYSTEM_PROMPT = `Je bent een documentontwerper voor een Nederlands ERP-systeem.
|
|
6
|
+
Je analyseert een bestaand document (PDF of afbeelding) en zet het om naar een herbruikbare layout.
|
|
7
|
+
Herken logo's, adresblokken, koppen, tabellen met kolommen, totalen, barcodes en voetteksten.
|
|
8
|
+
Vervang concrete gegevens door veldcodes zoals {{order.nummer}}, {{klant.naam}}, {{klant.adres}},
|
|
9
|
+
{{factuur.totaal}}, {{systeem.datum}}. Coördinaten en afmetingen zijn in millimeters, linksboven is 0,0.
|
|
10
|
+
Herken logo's als element van het type "logo". Je krijgt een lijst met logo's die al in de centrale
|
|
11
|
+
Logo Bibliotheek staan; als het herkende logo daar duidelijk bij past, zet dan die exacte naam in
|
|
12
|
+
"logo_naam". Past het bij geen enkel bekend logo, laat "logo_naam" dan leeg zodat het als nieuw logo
|
|
13
|
+
kan worden voorgesteld.
|
|
14
14
|
Antwoord uitsluitend met JSON.`;
|
|
15
15
|
const SCHEMA = {
|
|
16
16
|
type: "object",
|
|
@@ -26,7 +26,7 @@ export function paginaCss(formaat, orientatie, margeMm = 0) {
|
|
|
26
26
|
const gedraaid = formaat && orientatie === "landscape"
|
|
27
27
|
? `${formaat.hoogte}mm ${formaat.breedte}mm`
|
|
28
28
|
: size;
|
|
29
|
-
return `@page { size: ${gedraaid}; margin: ${margeMm}mm; }
|
|
29
|
+
return `@page { size: ${gedraaid}; margin: ${margeMm}mm; }
|
|
30
30
|
@media print { html, body { margin: 0; padding: 0; } }`;
|
|
31
31
|
}
|
|
32
32
|
/**
|
|
@@ -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;
|
|
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
|
-
|
|
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":"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;
|
|
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"}
|
|
@@ -525,6 +525,16 @@ async function meetHoogte(ctx, n, vak, s) {
|
|
|
525
525
|
await tekenKinderen(meetCtx, { tag: "div", attrs: {}, kinderen: [n] }, vak, s);
|
|
526
526
|
});
|
|
527
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
|
+
}
|
|
528
538
|
function rijenVan(tabel) {
|
|
529
539
|
const rijen = [];
|
|
530
540
|
const loop = (k) => {
|
|
@@ -562,12 +572,23 @@ async function tekenBlok(ctx, n, vak, ouderStijl) {
|
|
|
562
572
|
const s = stijlVan(n, ouderStijl);
|
|
563
573
|
const st = eig(n);
|
|
564
574
|
const cl = klassen(n);
|
|
565
|
-
const barcode =
|
|
575
|
+
const barcode = eigenBarcodeWaarde(n);
|
|
566
576
|
if (barcode) {
|
|
567
|
-
const
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
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;
|
|
571
592
|
return;
|
|
572
593
|
}
|
|
573
594
|
if (n.tag === "#tekst") {
|
|
@@ -695,7 +716,7 @@ async function tekenBlok(ctx, n, vak, ouderStijl) {
|
|
|
695
716
|
const heelHouden = cl.includes("heelhouden") || /avoid/.test(st["break-inside"] ?? st["page-break-inside"] ?? "");
|
|
696
717
|
const binnenVak = { x: vak.x + padding, breedte: vak.breedte - padding * 2 };
|
|
697
718
|
if (!ctx.meten && (heelHouden || achtergrond)) {
|
|
698
|
-
const nodig = (await
|
|
719
|
+
const nodig = (await meetInhoud(ctx, n, binnenVak, s)) + padding * 2;
|
|
699
720
|
if (heelHouden && nodig > tk.ruimte && nodig < tk.hoogte - tk.marge * 2) {
|
|
700
721
|
tk.nieuwePagina();
|
|
701
722
|
}
|
|
@@ -816,7 +837,18 @@ async function tekenTabel(ctx, tabel, vak, s) {
|
|
|
816
837
|
const cellen = rij.kinderen.filter((c) => c.tag === "td" || c.tag === "th");
|
|
817
838
|
const celBreedte = (i) => Math.max(4, (breedtes[i] ?? 20) - padX * 2);
|
|
818
839
|
const celTekst = cellen.map((c) => plattTekst(c));
|
|
819
|
-
|
|
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
|
+
}));
|
|
820
852
|
const teksten = cellen.map((_, i) => breekAf(tk, celTekst[i], cellenStijl[i], celBreedte(i)));
|
|
821
853
|
const hoogte = Math.max(regelHoogte(s), ...teksten.map((t, i) => Math.max(1, t.length) * regelHoogte(cellenStijl[i] ?? s))) + padY * 2;
|
|
822
854
|
if (hoogte > tk.ruimte) {
|
|
@@ -895,7 +927,7 @@ async function tekenAbsoluut(ctx, n, s) {
|
|
|
895
927
|
const hoogte = lengte(st["height"]) ?? regelHoogte(eigen);
|
|
896
928
|
const yBoven = tk.hoogte - top;
|
|
897
929
|
const yOnder = yBoven - hoogte;
|
|
898
|
-
const barcode =
|
|
930
|
+
const barcode = eigenBarcodeWaarde(n);
|
|
899
931
|
if (barcode) {
|
|
900
932
|
const barcodeHoogte = Math.max(5 * MM, hoogte * 0.72);
|
|
901
933
|
tekenBarcode(tk, barcode, x, breedte, barcodeHoogte, yBoven);
|
|
@@ -1063,6 +1095,24 @@ function zoekBarcodeWaarde(n) {
|
|
|
1063
1095
|
}
|
|
1064
1096
|
return null;
|
|
1065
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
|
+
}
|
|
1066
1116
|
/** Zoekt een streepjes- of blokjespatroon (barcode/QR) in de knoop of kinderen. */
|
|
1067
1117
|
function zoekGradient(n) {
|
|
1068
1118
|
const css = `${eig(n)["background"] ?? ""} ${eig(n)["background-image"] ?? ""}`;
|
|
@@ -1107,8 +1157,6 @@ async function tekenKinderen(ctx, n, vak, s) {
|
|
|
1107
1157
|
(/grid/.test(display) && kolommen.trim().split(/\s+/).length === 2);
|
|
1108
1158
|
const rijModus = /flex/.test(display) ||
|
|
1109
1159
|
cl.includes("kv") ||
|
|
1110
|
-
cl.includes("karkop") ||
|
|
1111
|
-
cl.includes("laagkop") ||
|
|
1112
1160
|
cl.includes("meta") ||
|
|
1113
1161
|
cl.includes("totalen") ||
|
|
1114
1162
|
cl.includes("regelrij");
|
|
@@ -1146,8 +1194,7 @@ async function tekenKinderen(ctx, n, vak, s) {
|
|
|
1146
1194
|
const spreiden = stukken.length > 1 &&
|
|
1147
1195
|
(verdeel === "space-between" ||
|
|
1148
1196
|
verdeel === "space-around" ||
|
|
1149
|
-
((!verdeel || verdeel === "normal") &&
|
|
1150
|
-
(cl.includes("kv") || cl.includes("karkop") || cl.includes("laagkop"))));
|
|
1197
|
+
((!verdeel || verdeel === "normal") && cl.includes("kv")));
|
|
1151
1198
|
const rechtsUit = verdeel === "flex-end" || verdeel === "right" || verdeel === "end";
|
|
1152
1199
|
if (spreiden) {
|
|
1153
1200
|
let x = vak.x;
|
|
@@ -1253,11 +1300,64 @@ async function laadAfbeeldingen(pdf, wortel) {
|
|
|
1253
1300
|
}));
|
|
1254
1301
|
return { beelden, fouten };
|
|
1255
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
|
+
}
|
|
1256
1345
|
/** Zet print-HTML om naar een PDF en levert die als base64 (zonder prefix). */
|
|
1257
1346
|
export async function htmlNaarPdfBytes(opties) {
|
|
1258
1347
|
const bMm = Number(opties.breedteMm ?? 210);
|
|
1259
1348
|
const hMm = Number(opties.hoogteMm ?? 297);
|
|
1260
|
-
const
|
|
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;
|
|
1261
1361
|
const breedte = (opties.landschap ? Math.max(bMm, hMm) : Math.min(bMm, hMm)) * MM;
|
|
1262
1362
|
const hoogte = (opties.landschap ? Math.min(bMm, hMm) : Math.max(bMm, hMm)) * MM;
|
|
1263
1363
|
const pdf = await PDFDocument.create();
|
|
@@ -1268,6 +1368,9 @@ export async function htmlNaarPdfBytes(opties) {
|
|
|
1268
1368
|
// het document er hetzelfde uitziet als in het voorbeeld op het scherm.
|
|
1269
1369
|
stempelCss(wortel, ontleedCss(leesStylesheets(opties.html)));
|
|
1270
1370
|
const { beelden: afbeeldingen, fouten: beeldFouten } = await laadAfbeeldingen(pdf, wortel);
|
|
1371
|
+
if (beeldFouten.size) {
|
|
1372
|
+
throw new Error(`Afdrukafwijking gevonden: ${[...beeldFouten.values()].join(" ")}`);
|
|
1373
|
+
}
|
|
1271
1374
|
const basis = basisGrootte(opties.html);
|
|
1272
1375
|
const tk = new Tekenaar(pdf, breedte, hoogte, marge, normaal, vet);
|
|
1273
1376
|
const ctx = { tk, afbeeldingen, beeldFouten };
|
|
@@ -1292,8 +1395,8 @@ export async function htmlNaarPdfBytes(opties) {
|
|
|
1292
1395
|
}
|
|
1293
1396
|
// De layout bepaalt of paginanummers meegaan; zonder voorkeur alleen bij
|
|
1294
1397
|
// meerdere pagina's.
|
|
1295
|
-
const
|
|
1296
|
-
const nummers = opties.paginanummers ?? (
|
|
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);
|
|
1297
1400
|
if (nummers) {
|
|
1298
1401
|
tk.paginas.forEach((p, i) => {
|
|
1299
1402
|
const t = `Pagina ${i + 1} van ${tk.paginas.length}`;
|
|
@@ -81,8 +81,8 @@ function elementHtml(el, ctx) {
|
|
|
81
81
|
return `<div style="${base};border-radius:50%;border:${s.randbreedte || 0.3}mm solid ${s.randkleur ?? "#111827"}"></div>`;
|
|
82
82
|
case "barcode":
|
|
83
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>
|
|
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
86
|
<div style="font-size:7pt">${esc(inhoud)}</div></div>`
|
|
87
87
|
: "";
|
|
88
88
|
case "qrcode":
|
|
@@ -128,10 +128,10 @@ export function bouwDocumentHtml(layout, elementen, gegevens) {
|
|
|
128
128
|
const body = zichtbaar.map((e) => elementHtml(e, ctx)).join("");
|
|
129
129
|
const b = layout.orientatie === "landscape" ? layout.hoogte_mm : layout.breedte_mm;
|
|
130
130
|
const h = layout.orientatie === "landscape" ? layout.breedte_mm : layout.hoogte_mm;
|
|
131
|
-
return `<!doctype html><html lang="nl"><head><meta charset="utf-8"><title>${esc(layout.naam)}</title>
|
|
132
|
-
<style>@page{size:${b}mm ${h}mm;margin:0}
|
|
133
|
-
body{margin:0;background:#e5e7eb}
|
|
134
|
-
.pagina{position:relative;width:${b}mm;height:${h}mm;background:#fff;margin:0 auto;overflow:hidden}
|
|
135
|
-
@media print{body{background:#fff}}</style></head>
|
|
131
|
+
return `<!doctype html><html lang="nl"><head><meta charset="utf-8"><title>${esc(layout.naam)}</title>
|
|
132
|
+
<style>@page{size:${b}mm ${h}mm;margin:0}
|
|
133
|
+
body{margin:0;background:#e5e7eb}
|
|
134
|
+
.pagina{position:relative;width:${b}mm;height:${h}mm;background:#fff;margin:0 auto;overflow:hidden}
|
|
135
|
+
@media print{body{background:#fff}}</style></head>
|
|
136
136
|
<body><div class="pagina">${body}</div></body></html>`;
|
|
137
137
|
}
|
|
@@ -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,
|
|
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"}
|
|
@@ -334,22 +334,49 @@ export async function laadPakbonData(db, opties) {
|
|
|
334
334
|
const items = karItems.filter((i) => i["cart_id"] === kar["id"]);
|
|
335
335
|
const maxLagen = Math.max(1, num(kar["max_layers"]) || 0) || 1;
|
|
336
336
|
const lagen = [];
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
const perLaag =
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
1;
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
337
|
+
// De Logistiek Engine geeft per karregel het laagnummer door. Zodra dat
|
|
338
|
+
// aanwezig is volgen we die indeling exact — nooit zelf herverdelen.
|
|
339
|
+
const engineLagen = items.some((i) => num(i["layer_index"]) > 0);
|
|
340
|
+
if (engineLagen) {
|
|
341
|
+
const perLaag = new Map();
|
|
342
|
+
for (const item of items) {
|
|
343
|
+
const fusten = num(item["crates"]);
|
|
344
|
+
const perFust = num(item["plants_per_crate"]);
|
|
345
|
+
const nummer = Math.max(1, num(item["layer_index"]) || 1);
|
|
346
|
+
const capaciteit = num(item["crates_per_layer"]);
|
|
347
|
+
const inLaag = num(item["crates_in_layer"]) || fusten;
|
|
348
|
+
const deel = capaciteit > 0 ? inLaag / capaciteit : num(item["layer_fraction"]) || 0;
|
|
349
|
+
let laag = perLaag.get(nummer);
|
|
350
|
+
if (!laag) {
|
|
351
|
+
laag = { producten: [], ruimte: 0 };
|
|
352
|
+
perLaag.set(nummer, laag);
|
|
353
|
+
}
|
|
354
|
+
laag.producten.push(maakProduct(item, fusten * perFust || num(item["boxes"]), fusten, perFust));
|
|
355
|
+
laag.ruimte += deel;
|
|
356
|
+
}
|
|
357
|
+
const hoogste = Math.max(maxLagen, ...perLaag.keys());
|
|
358
|
+
for (let n = 1; n <= hoogste; n++) {
|
|
359
|
+
lagen.push(perLaag.get(n) ?? { producten: [], ruimte: 0 });
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
let huidige = null;
|
|
364
|
+
for (const item of items) {
|
|
365
|
+
const fusten = num(item["crates"]);
|
|
366
|
+
const perFust = num(item["plants_per_crate"]);
|
|
367
|
+
const perLaag = num(item["crates_per_layer"]);
|
|
368
|
+
const deel = num(item["layer_fraction"]) ||
|
|
369
|
+
(perLaag > 0 ? fusten / perLaag : 0) ||
|
|
370
|
+
(num(item["layer_share_pct"]) ? num(item["layer_share_pct"]) / 100 : 0) ||
|
|
371
|
+
1;
|
|
372
|
+
const product = maakProduct(item, fusten * perFust || num(item["boxes"]), fusten, perFust);
|
|
373
|
+
if (!huidige || huidige.ruimte + deel > 1.0001) {
|
|
374
|
+
huidige = { producten: [], ruimte: 0 };
|
|
375
|
+
lagen.push(huidige);
|
|
376
|
+
}
|
|
377
|
+
huidige.producten.push(product);
|
|
378
|
+
huidige.ruimte += deel;
|
|
350
379
|
}
|
|
351
|
-
huidige.producten.push(product);
|
|
352
|
-
huidige.ruimte += deel;
|
|
353
380
|
}
|
|
354
381
|
if (!lagen.length)
|
|
355
382
|
lagen.push({ producten: [], ruimte: 0 });
|
|
@@ -365,7 +392,7 @@ export async function laadPakbonData(db, opties) {
|
|
|
365
392
|
gewicht: kar["weight_kg"] ? `${num(kar["weight_kg"]).toFixed(1)} kg` : "",
|
|
366
393
|
gedeeld: kar["__gedeeld"] === true,
|
|
367
394
|
opmerking: String(kar["notes"] ?? ""),
|
|
368
|
-
lagen: lagen.
|
|
395
|
+
lagen: lagen.map((laag, j) => {
|
|
369
396
|
const potmaten = uniek(laag.producten.map((product) => product["potmaat"]));
|
|
370
397
|
return {
|
|
371
398
|
laagnummer: j + 1,
|