156 lines
6.2 KiB
JavaScript
156 lines
6.2 KiB
JavaScript
/*!
|
|
* Tectum
|
|
* Copyright © 2026 Machiel Heinen.
|
|
* Alle rechten voorbehouden. Gebruik uitsluitend met schriftelijke toestemming.
|
|
* Zie LICENTIE.md.
|
|
*/
|
|
// ============================================================
|
|
// core/pdf-sheet.js — gedeelde bouwstenen voor de A3-tekenbladen (jsPDF):
|
|
// kleuren, nieuw document, maatlijn en titelblok. Gebruikt door WallPdf,
|
|
// FloorPdf en de unit-PDF, zodat elk blad er identiek uitziet.
|
|
// Plain script (geen ES-module), geladen na jspdf.umd.min.js.
|
|
// ============================================================
|
|
|
|
window.PdfSheet = (() => {
|
|
// Kleurlogica: rood = houtwerk-omtrekken, blauw = hoofdmaatvoering,
|
|
// zwart = opening-kruis, posnum-labels en de onderste maatketens.
|
|
const COLORS = { RED: [190, 25, 25], BLUE: [25, 50, 180], BLACK: [0, 0, 0], GREY: [130, 130, 130] };
|
|
|
|
// Veldnamen van de "Project eigenschappen"-modal van de wand-/vloer-editor.
|
|
const PROJECT_DOM_IDS = {
|
|
naam: 'inp-proj-naam', plaats: 'inp-proj-plaats', straat: 'inp-proj-straat', nummer: 'inp-proj-nummer',
|
|
tekenaar: 'inp-proj-tekenaar', titel: 'inp-proj-titel', element: 'inp-proj-element', opmerking: 'inp-proj-opmerking'
|
|
};
|
|
|
|
// Afbeeldingen (bijv. textuur-voorvertoningen) voor in de PDF. generate()
|
|
// is synchroon, dus vooraf laden (preloadImage, bijv. zodra een materiaal
|
|
// gekozen is); imageData geeft de data-URL, of null als hij (nog) niet
|
|
// geladen is (dan tekent de aanroeper een kleurvlak).
|
|
const imageCache = {};
|
|
function preloadImage(url) {
|
|
if (!url || url in imageCache || typeof Image === 'undefined' || typeof document === 'undefined') return;
|
|
imageCache[url] = null;
|
|
const img = new Image();
|
|
img.onload = () => {
|
|
try {
|
|
const c = document.createElement('canvas');
|
|
c.width = img.naturalWidth; c.height = img.naturalHeight;
|
|
c.getContext('2d').drawImage(img, 0, 0);
|
|
imageCache[url] = c.toDataURL('image/png');
|
|
} catch (e) { /* laat null: kleurvlak */ }
|
|
};
|
|
img.src = url;
|
|
}
|
|
const imageData = (url) => (url && imageCache[url]) || null;
|
|
|
|
function createDoc() {
|
|
const { jsPDF } = window.jspdf;
|
|
return new jsPDF({ orientation: 'landscape', unit: 'mm', format: 'a3' });
|
|
}
|
|
|
|
function readProjectFromDom() {
|
|
const project = {};
|
|
Object.entries(PROJECT_DOM_IDS).forEach(([key, id]) => {
|
|
const el = document.getElementById(id);
|
|
project[key] = (el && el.value) || '';
|
|
});
|
|
return project;
|
|
}
|
|
|
|
// Maatlijn met kleine loodrechte eindstreepjes ("|") op beide
|
|
// uiteinden, zodat duidelijk is waar de maat precies begint/eindigt
|
|
// i.p.v. een lijn die zonder markering in de lucht lijkt te eindigen.
|
|
const DIM_TICK = 1.5;
|
|
function dimLine(doc, x1, y1, x2, y2) {
|
|
doc.line(x1, y1, x2, y2);
|
|
const dx = x2 - x1, dy = y2 - y1;
|
|
const len = Math.hypot(dx, dy) || 1;
|
|
const tx = (-dy / len) * (DIM_TICK / 2), ty = (dx / len) * (DIM_TICK / 2);
|
|
doc.line(x1 - tx, y1 - ty, x1 + tx, y1 + ty);
|
|
doc.line(x2 - tx, y2 - ty, x2 + tx, y2 + ty);
|
|
}
|
|
|
|
// Titelblok (rechtsonder), in twee kolommen zodat het weinig hoogte
|
|
// inneemt, met het logo (core/logo-data.js) rechtsonder.
|
|
// project: { naam, plaats, straat, nummer, tekenaar, opmerking };
|
|
// opts: { titel, element, blad } (blad bijv. "2 / 7").
|
|
function titleBlock(doc, project, opts) {
|
|
const { BLACK, GREY } = COLORS;
|
|
const p = project || {};
|
|
const o = opts || {};
|
|
const tbW = 118, tbH = 36;
|
|
const tbX0 = 290, tbY0 = 289 - tbH;
|
|
doc.setDrawColor(...BLACK);
|
|
doc.setLineWidth(0.25);
|
|
doc.rect(tbX0, tbY0, tbW, tbH, 'S');
|
|
|
|
const today = new Date().toLocaleDateString('nl-NL');
|
|
const columns = [
|
|
[['Projectnaam:', p.naam], ['Plaats:', p.plaats], ['Straat:', p.straat], ['Projectnummer:', p.nummer], ['Tekenaar:', p.tekenaar]],
|
|
[['Titel:', o.titel], ['Element nummer:', o.element], ['Datum getekend:', today], ['Schaal:', '1:1'], ['Opmerking:', p.opmerking]]
|
|
];
|
|
const colW = tbW / 2, labelW = 19;
|
|
doc.setFontSize(6.2);
|
|
columns.forEach((rows, c) => {
|
|
const x = tbX0 + 3 + c * colW;
|
|
let rowY = tbY0 + 5;
|
|
rows.forEach(([label, value]) => {
|
|
doc.setTextColor(...GREY);
|
|
doc.text(label, x, rowY);
|
|
doc.setTextColor(...BLACK);
|
|
const text = String(value || '-');
|
|
doc.text(doc.splitTextToSize(text, colW - labelW - 5)[0], x + labelW, rowY);
|
|
rowY += 4.2;
|
|
});
|
|
});
|
|
|
|
// Scheidingslijn + onderstrook: blad links, logo rechts.
|
|
const stripY = tbY0 + 25;
|
|
doc.setDrawColor(...GREY);
|
|
doc.setLineWidth(0.15);
|
|
doc.line(tbX0, stripY, tbX0 + tbW, stripY);
|
|
if (o.blad) {
|
|
doc.setTextColor(...GREY);
|
|
doc.setFontSize(6);
|
|
doc.text('Blad ' + o.blad, tbX0 + 3, tbY0 + tbH - 3.5);
|
|
}
|
|
const logoH = 7.5, logoW = logoH * (window.TECTUM_LOGO ? window.TECTUM_LOGO.aspect : 4.55);
|
|
if (window.TECTUM_LOGO) {
|
|
doc.addImage(window.TECTUM_LOGO.png, 'PNG', tbX0 + tbW - 3 - logoW, tbY0 + tbH - 2 - logoH, logoW, logoH, 'tectum-logo', 'FAST'); // alias: 1x in het bestand, gecomprimeerd
|
|
} else {
|
|
doc.setTextColor(...GREY);
|
|
doc.setFontSize(6);
|
|
doc.text('Van Emous Mantelzorgwoningen', tbX0 + tbW - 3, tbY0 + tbH - 3.5, { align: 'right' });
|
|
}
|
|
}
|
|
|
|
// Eenvoudige tabel: headers + rijen (arrays van strings) vanaf (x, y) met
|
|
// vaste kolom-x'en. Geeft de y-positie onder de tabel terug.
|
|
function table(doc, x, y, colX, headers, rows, opts) {
|
|
const { BLACK, GREY } = COLORS;
|
|
const o = opts || {};
|
|
const rowH = o.rowH || 4.3;
|
|
doc.setTextColor(...BLACK);
|
|
if (o.title) {
|
|
doc.setFontSize(o.titleSize || 9);
|
|
doc.text(o.title, x, y);
|
|
y += 6;
|
|
}
|
|
doc.setFontSize(o.fontSize || 6.5);
|
|
headers.forEach((h, i) => doc.text(h, x + colX[i], y));
|
|
y += 1.5;
|
|
doc.setDrawColor(...GREY);
|
|
doc.setLineWidth(0.15);
|
|
doc.line(x, y, x + colX[colX.length - 1] + (o.lastColW || 14), y);
|
|
y += 4;
|
|
rows.forEach(r => {
|
|
if (o.maxY && y > o.maxY) return;
|
|
r.forEach((v, i) => doc.text(String(v), x + colX[i], y, i > 0 && o.alignRight ? { align: 'right' } : undefined));
|
|
y += rowH;
|
|
});
|
|
return y;
|
|
}
|
|
|
|
return { COLORS, preloadImage, imageData, createDoc, readProjectFromDom, dimLine, titleBlock, table };
|
|
})();
|