From ed25373b1c95e002502311646605790193f135f9 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Thu, 23 Oct 2025 01:41:37 +0200 Subject: [PATCH] Add consent paragraphs and formatting to fillable PDF; update signature section and save new version --- scripts/create-fillable-template.js | 106 ++++++++++++++++++- server/templates/mitgliedschaft-fillable.pdf | Bin 12768 -> 14417 bytes 2 files changed, 102 insertions(+), 4 deletions(-) diff --git a/scripts/create-fillable-template.js b/scripts/create-fillable-template.js index 20582db..9d95fff 100644 --- a/scripts/create-fillable-template.js +++ b/scripts/create-fillable-template.js @@ -323,10 +323,8 @@ async function create() { page2.drawRectangle({ x: sepaLeft, y: signLineY - 2, width: signLineWidth, height: 1, color: rgb(0,0,0) }) page2.drawText('Unterschrift des Kontoinhabers', { x: sepaLeft, y: signLineY - 18, size: 11, font: helv }) // no form field for signature on page 2 (physical signature) - - const pdfBytes = await pdfDoc.save() - fs.writeFileSync('server/templates/mitgliedschaft-fillable.pdf', pdfBytes) - console.log('Wrote server/templates/mitgliedschaft-fillable.pdf') + page2.drawText('Unterschrift des Kontoinhabers', { x: sepaLeft, y: signLineY - 18, size: 11, font: helv }) + // no form field for signature on page 2 (physical signature) // --- Add a third page with same header/bar/footer and title 'Einwilligungserklärung' --- const page3 = pdfDoc.addPage([595.28, 841.89]) @@ -349,6 +347,106 @@ async function create() { const footerSize3 = 10 const footerWidth3 = helv.widthOfTextAtSize(footerText3, footerSize3) page3.drawText(footerText3, { x: width3 - footerWidth3 - leftX, y: 56.7, size: footerSize3, font: helv }) + + // --- consent paragraphs on page 3 (wrapped) --- + const consentText = `für die Veröffentlichung von Mitgliederdaten im Internet. + +Der Vereinsvorstand weist hiermit darauf hin, dass ausreichende technische Maßnahmen zur Gewährleistung des Datenschutzes getroffen wurden. Dennoch kann bei einer Veröffentlichung von personenbezogenen Mitgliederdaten im Internet ein umfassender Datenschutz nicht garantiert werden. +Daher nimmt das Vereinsmitglied die Risiken für eine eventuelle Persönlichkeitsrechtsverletzung zur +Kenntnis und ist sich bewusst, dass: +• die personenbezogenen Daten auch in Staaten abrufbar sind, die keine der Bundesrepublik Deutschland vergleichbaren Datenschutzbestimmungen kennen, +• die Vertraulichkeit, die Integrität (Unverletzlichkeit), die Authentizität (Echtheit) und die Verfügbarkeit der personenbezogenen Daten nicht garantiert ist. + +Das Vereinsmitglied trifft die Entscheidung zur Veröffentlichung seiner Daten im Internet freiwillig und kann seine Einwilligung gegenüber dem Vereinsvorstand jederzeit widerrufen.` + + // helper for wrapped text on page3 + const wrapMaxWidth3 = width3 - leftX - 48 + function drawWrappedOnPage3(text, x, y, size, font) { + // handle bullet lines: split by newline to preserve existing bullet markers + const lines = text.split(/\r?\n/) + let curY = y + const indent = 14 // indent for text following bullet + for (const rawLine of lines) { + const line = rawLine.trim() + if (!line) { curY -= size + 4; continue } + if (line.startsWith('•')) { + // draw bullet circle and then wrap remainder with indentation + const after = line.replace(/^•\s?/, '') + // bullet position + const bx = x + 4 + // place bullet aligned with the first text line (slightly above center) + const by = curY - size * 0.25 + page3.drawCircle({ x: bx, y: by, size: 1.8, color: rgb(0,0,0) }) + // wrap the 'after' text with indent + const words = after.replace(/\s+/g, ' ').trim().split(' ') + let curLine = '' + for (const w of words) { + const test = curLine ? curLine + ' ' + w : w + const testWidth = helv.widthOfTextAtSize(test, size) + if (testWidth > wrapMaxWidth3 - indent) { + page3.drawText(curLine, { x: x + indent, y: curY, size, font }) + curLine = w + curY -= size + 4 + } else { + curLine = test + } + } + if (curLine) { + page3.drawText(curLine, { x: x + indent, y: curY, size, font }) + curY -= size + 4 + } + } else { + // regular paragraph line: wrap normally + const words = line.replace(/\s+/g, ' ').trim().split(' ') + let curLine = '' + for (const w of words) { + const test = curLine ? curLine + ' ' + w : w + const testWidth = helv.widthOfTextAtSize(test, size) + if (testWidth > wrapMaxWidth3) { + page3.drawText(curLine, { x, y: curY, size, font }) + curLine = w + curY -= size + 4 + } else { + curLine = test + } + } + if (curLine) { + page3.drawText(curLine, { x, y: curY, size, font }) + curY -= size + 4 + } + } + } + return curY + } + + // split into paragraphs and draw with 0.5cm (~14.17pt) paragraph spacing + const paras = consentText.split('\n\n') + let py = page3TitleY - 28 + const paraSpacing = 14.17 + const paraSize = 11 + for (const p of paras) { + py = drawWrappedOnPage3(p.trim(), leftX, py, paraSize, helv) + py -= paraSpacing + } + + // Additional confirmation block (formatted) with 0.4cm spacing + const blockSpacing = 11.34 // 0.4 cm + const boldSize = 12 + const normalSize = 11 + // ensure a bit of space before the block + py -= blockSpacing + py = drawWrappedOnPage3('Erklärung:', leftX, py, boldSize, helvBold) + py -= blockSpacing + py = drawWrappedOnPage3('Ich bestätige das Vorstehende zur Kenntnis genommen zu haben und willige ein, dass der', leftX, py, normalSize, helv) + py -= blockSpacing + py = drawWrappedOnPage3('Harheimer Tischtennis-Club 1954 e.V.', leftX, py, boldSize, helvBold) + py -= blockSpacing + py = drawWrappedOnPage3('folgende allgemeine Daten zu meiner Person:', leftX, py, normalSize, helv) + py -= blockSpacing + + const pdfBytes = await pdfDoc.save() + fs.writeFileSync('server/templates/mitgliedschaft-fillable.pdf', pdfBytes) + console.log('Wrote server/templates/mitgliedschaft-fillable.pdf') } create().catch(e => { diff --git a/server/templates/mitgliedschaft-fillable.pdf b/server/templates/mitgliedschaft-fillable.pdf index 02a91b6666f5910943b2c79b3dc6056dda555440..f9021d745e4124480e285aedc4c23f79c9439c2e 100644 GIT binary patch delta 4854 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 delta 3276 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 KHVP#rMNdZFc}p|^