Refactor environment configuration for local development; update SMTP settings and add JWT secret, encryption key, and debug options. Enhance Nuxt configuration for development server and runtime settings. Introduce new membership application form with validation and PDF generation functionality. Update footer and navigation components to include new membership links. Revise user and session data in JSON files.
This commit is contained in:
357
scripts/create-fillable-template.js
Normal file
357
scripts/create-fillable-template.js
Normal file
@@ -0,0 +1,357 @@
|
||||
import { PDFDocument, StandardFonts, rgb } from 'pdf-lib'
|
||||
import fs from 'fs'
|
||||
|
||||
async function create() {
|
||||
const pdfDoc = await PDFDocument.create()
|
||||
const page = pdfDoc.addPage([595.28, 841.89]) // A4
|
||||
const helv = await pdfDoc.embedFont(StandardFonts.Helvetica)
|
||||
const helvBold = await pdfDoc.embedFont(StandardFonts.HelveticaBold)
|
||||
const { width, height } = page.getSize()
|
||||
|
||||
// left column moved further left to align with checkboxes
|
||||
const leftX = 48
|
||||
const rightX = 320
|
||||
// baseY is the vertical anchor for page-1 content. Increase it by ~5.2mm (≈14.739pt)
|
||||
const baseY = height - 160 + 14.739
|
||||
// shift to apply only to labels and input fields (move those 1cm down)
|
||||
const labelsShift = -28.35
|
||||
const gap = 36
|
||||
const labelWidth = 100
|
||||
const fieldXOffset = 100
|
||||
// Make fields 10% narrower
|
||||
const fieldWidth = Math.round(180 * 0.9)
|
||||
// shrink most fields by another 20% on request (applied selectively later)
|
||||
const fieldShrinkFactor = 0.8
|
||||
const fieldHeight = 14
|
||||
// raise date fields by 0.4cm (≈11.34pt) when requested; add extra 1.2mm (≈3.4016pt) per user request
|
||||
const dateRaise = 11.34 + 3.4016 // now ≈14.7416pt
|
||||
// fixed checkbox positions (do not move with leftX)
|
||||
const cbX = 48
|
||||
const cbYOffset = 5
|
||||
|
||||
// Header: centered club name and a full-width horizontal bar underneath (~2mm high)
|
||||
const headerText = 'Harheimer Tischtennis-Club 1954 e.V.'
|
||||
const headerSize = 20
|
||||
const textWidth = helv.widthOfTextAtSize(headerText, headerSize)
|
||||
const headerX = (width - textWidth) / 2
|
||||
const headerY = height - 72
|
||||
page.drawText(headerText, { x: headerX, y: headerY, size: headerSize, font: helv })
|
||||
// draw full-width bar directly under the header; 2mm ≈ 5.67 points
|
||||
const barHeight = 5.67
|
||||
const barY = headerY - 20
|
||||
page.drawRectangle({ x: 0, y: barY, width: width, height: barHeight, color: rgb(0,0,0) })
|
||||
|
||||
// Labels and lines
|
||||
// Labels left-aligned in their columns
|
||||
// Add form title above the fields
|
||||
const titleText = 'Beitrittserklärung'
|
||||
const titleSize = 14
|
||||
const titleWidth = helvBold.widthOfTextAtSize(titleText, titleSize)
|
||||
// left-align title above the left labels and move up ~0.7cm (≈20pt)
|
||||
const titleX = leftX
|
||||
// move title down by 0.5cm (≈14.17pt)
|
||||
const titleY = baseY + 24 + 20 - 14.17
|
||||
page.drawText(titleText, { x: titleX, y: titleY, size: titleSize, font: helvBold })
|
||||
|
||||
const subtitle = 'Hiermit beantrage ich,'
|
||||
const subtitleSize = 12
|
||||
const subtitleX = leftX
|
||||
const subtitleY = titleY - 18 - 14.17
|
||||
page.drawText(subtitle, { x: subtitleX, y: subtitleY, size: subtitleSize, font: helv })
|
||||
// apply same vertical shift as fields so labels align
|
||||
const fieldsShift = -14.17
|
||||
page.drawText('Name:', { x: leftX, y: baseY + fieldsShift + labelsShift, size: 12, font: helv })
|
||||
page.drawText('Vorname:', { x: rightX, y: baseY + fieldsShift + labelsShift, size: 12, font: helv })
|
||||
|
||||
page.drawText('Straße:', { x: leftX, y: baseY - gap + fieldsShift + labelsShift, size: 12, font: helv })
|
||||
page.drawText('PLZ/Wohnort:', { x: rightX, y: baseY - gap + fieldsShift + labelsShift, size: 12, font: helv })
|
||||
|
||||
page.drawText('Geburtsdatum:', { x: leftX, y: baseY - gap * 2 + fieldsShift + labelsShift, size: 12, font: helv })
|
||||
page.drawText('Telefon(privat):', { x: rightX, y: baseY - gap * 2 + fieldsShift + labelsShift, size: 12, font: helv })
|
||||
|
||||
page.drawText('E-Mail:', { x: leftX, y: baseY - gap * 3 + fieldsShift + labelsShift, size: 12, font: helv })
|
||||
page.drawText('Telefon(Mobil):', { x: rightX, y: baseY - gap * 3 + fieldsShift + labelsShift, size: 12, font: helv })
|
||||
|
||||
// Create form fields
|
||||
const form = pdfDoc.getForm()
|
||||
// Place fields on the same baseline as their labels
|
||||
// We need to move only the input fields on page 1 up by 0.6cm (≈17.01pt) without moving labels.
|
||||
const labelToFieldYDelta = 2 // small vertical offset so field baseline matches label visually
|
||||
const lift = 0 // original lift value
|
||||
// previously raised inputs by 17.01pt (0.6cm); move them down by 5.67pt (0.2cm)
|
||||
const inputFieldRaise = 11.34 // net upward offset now ~11.34pt
|
||||
form.createTextField('nachname').addToPage(page, { x: leftX + fieldXOffset, y: baseY - fieldHeight + labelToFieldYDelta + lift + fieldsShift + labelsShift + inputFieldRaise, width: Math.round(fieldWidth * fieldShrinkFactor), height: fieldHeight, font: helv })
|
||||
form.createTextField('vorname').addToPage(page, { x: rightX + fieldXOffset, y: baseY - fieldHeight + labelToFieldYDelta + lift + fieldsShift + labelsShift + inputFieldRaise, width: Math.round(fieldWidth * fieldShrinkFactor), height: fieldHeight, font: helv })
|
||||
form.createTextField('strasse').addToPage(page, { x: leftX + fieldXOffset, y: baseY - gap - fieldHeight + labelToFieldYDelta + lift + fieldsShift + labelsShift + inputFieldRaise, width: Math.round(fieldWidth * fieldShrinkFactor), height: fieldHeight, font: helv })
|
||||
form.createTextField('plz_ort').addToPage(page, { x: rightX + fieldXOffset, y: baseY - gap - fieldHeight + labelToFieldYDelta + lift + fieldsShift + labelsShift + inputFieldRaise, width: Math.round(fieldWidth * fieldShrinkFactor), height: fieldHeight, font: helv })
|
||||
form.createTextField('geburtsdatum').addToPage(page, { x: leftX + fieldXOffset, y: baseY - gap * 2 - fieldHeight + labelToFieldYDelta + lift + fieldsShift + labelsShift + inputFieldRaise, width: Math.round(fieldWidth * fieldShrinkFactor), height: fieldHeight, font: helv })
|
||||
form.createTextField('telefon').addToPage(page, { x: rightX + fieldXOffset, y: baseY - gap * 2 - fieldHeight + labelToFieldYDelta + lift + fieldsShift + labelsShift + inputFieldRaise, width: Math.round(fieldWidth * fieldShrinkFactor), height: fieldHeight, font: helv })
|
||||
form.createTextField('email').addToPage(page, { x: leftX + fieldXOffset, y: baseY - gap * 3 - fieldHeight + labelToFieldYDelta + lift + fieldsShift + labelsShift + inputFieldRaise, width: Math.round(fieldWidth * fieldShrinkFactor), height: fieldHeight, font: helv })
|
||||
form.createTextField('telefon_mobil').addToPage(page, { x: rightX + fieldXOffset, y: baseY - gap * 3 - fieldHeight + labelToFieldYDelta + lift + fieldsShift + labelsShift + inputFieldRaise, width: Math.round(fieldWidth * fieldShrinkFactor), height: fieldHeight, font: helv })
|
||||
|
||||
// read membership amounts from config (fall back to defaults)
|
||||
let erw = 120, jug = 72, passv = 30
|
||||
try {
|
||||
const cfg = JSON.parse(fs.readFileSync('server/data/config.json', 'utf8'))
|
||||
const ms = cfg.mitgliedschaft || []
|
||||
erw = (ms.find(m => /Erwachsene/i.test(m.typ)) || ms.find(m => m.typ === 'Erwachsene') || {}).preis || erw
|
||||
jug = (ms.find(m => /Jugend|Kinder/i.test(m.typ)) || ms.find(m => m.typ === 'Kinder/Jugend') || {}).preis || jug
|
||||
passv = (ms.find(m => /Passiv/i.test(m.typ)) || {}).preis || passv
|
||||
} catch (e) {
|
||||
console.error('Could not read config for membership amounts', e)
|
||||
}
|
||||
|
||||
const paraLines = [
|
||||
'Den derzeitigen jährlichen Mitgliedsbeitrag in Höhe von',
|
||||
`€ ${erw},-- (Erwachsene)`,
|
||||
`€ ${jug},-- (Jugendliche bis zum vollendeten 18. Lebensjahr)`,
|
||||
`€ ${passv},-- (passive Mitglieder)`,
|
||||
'bitte ich per Lastschrift jährlich von meinem Konto einzuziehen.',
|
||||
'Hierzu erteile ich beigefügtes SEPA-Lastschriftmandat.',
|
||||
'Mir ist bekannt, dass die Mitgliedschaft im Harheimer Tischtennis-Club erst nach Bestätigung durch den',
|
||||
'Vorstand Wirksamkeit erlangt. Die Beitragspflicht beginnt mit dem darauf folgenden Monat.',
|
||||
'Ich erkenne die Vereinssatzung (erhältlich beim Vorstand bzw. auf der Vereinshomepage) an.'
|
||||
]
|
||||
|
||||
// compute intermediate line position and move it down by additional 0.3cm ≈ 8.5pt
|
||||
const intermediateY = baseY - gap * 3 + fieldsShift - 6 - 14.17 - 8.5 - 8.5 + labelsShift
|
||||
page.drawText('meine Aufnahme in den Harheimer Tischtennis-Club 1954 e.V. als', { x: leftX, y: intermediateY, size: 12, font: helv })
|
||||
|
||||
// membership checkboxes
|
||||
// Keep checkbox labels and boxes at fixed left positions, but move them down by 0.8cm ≈ 22.68pt
|
||||
const cbLift = -22.68
|
||||
page.drawText('aktives Mitglied', { x: cbX + 16, y: baseY - gap * 5 + cbLift + labelsShift, size: 12, font: helv })
|
||||
page.drawText('passives Mitglied', { x: cbX + 16, y: baseY - gap * 6 + cbLift + labelsShift, size: 12, font: helv })
|
||||
form.createCheckBox('mitglied_aktiv').addToPage(page, { x: cbX, y: baseY - gap * 5 - cbYOffset + cbLift + labelsShift, width: 12, height: 12 })
|
||||
form.createCheckBox('mitglied_passiv').addToPage(page, { x: cbX, y: baseY - gap * 6 - cbYOffset + cbLift + labelsShift, width: 12, height: 12 })
|
||||
|
||||
// place the paragraph below the checkboxes with extra spacing before and after the cost lines (~0.3cm)
|
||||
try {
|
||||
// increase gap around cost lines for better readability (~0.6cm)
|
||||
const beforeCostGap = 17
|
||||
const afterCostGap = 17
|
||||
const paraStartY = baseY - gap * 6 + cbLift - 28 - beforeCostGap
|
||||
const lineHeight = 14
|
||||
// insert explicit gaps before and after the cost lines (lines 1-3)
|
||||
let y = paraStartY
|
||||
// first line
|
||||
page.drawText(paraLines[0], { x: leftX, y: y, size: 11, font: helv })
|
||||
y -= lineHeight
|
||||
// gap before costs
|
||||
y -= beforeCostGap
|
||||
// cost lines (1..3)
|
||||
for (let k = 1; k <= 3; k++) {
|
||||
page.drawText(paraLines[k], { x: leftX + 6, y: y, size: 11, font: helv })
|
||||
y -= lineHeight
|
||||
}
|
||||
// gap after costs
|
||||
y -= afterCostGap
|
||||
// remaining lines
|
||||
for (let i = 4; i < paraLines.length; i++) {
|
||||
page.drawText(paraLines[i], { x: leftX, y: y, size: 11, font: helv })
|
||||
y -= lineHeight
|
||||
}
|
||||
// shift following content down by afterCostGap if needed (none after currently)
|
||||
// now add signature/city line 2 cm below the current text
|
||||
const twoCm = 56.7
|
||||
const signY = y - twoCm
|
||||
const sigText = 'Frankfurt/Main-Harheim, den'
|
||||
const sigSize = 12
|
||||
page.drawText(sigText + ' ', { x: leftX, y: signY, size: sigSize, font: helv })
|
||||
const sigStartX = leftX + helv.widthOfTextAtSize(sigText + ' ', sigSize)
|
||||
const sigWidth = 220
|
||||
// draw thin line for signature
|
||||
page.drawRectangle({ x: sigStartX, y: signY - 2, width: sigWidth, height: 1, color: rgb(0,0,0) })
|
||||
// draw 'Datum' centered under the signature line
|
||||
const datum = 'Datum'
|
||||
const datumSize = 11
|
||||
const datumX = sigStartX + sigWidth / 2 - helv.widthOfTextAtSize(datum, datumSize) / 2
|
||||
page.drawText(datum, { x: datumX, y: signY - 18, size: datumSize, font: helv })
|
||||
// create a date form field on page 1 centered under the date line
|
||||
const dateFieldWidth = 120
|
||||
const dateFieldX = sigStartX + sigWidth / 2 - dateFieldWidth / 2
|
||||
// position date field so its bottom edge is exactly on the signature line
|
||||
const signatureLineY = signY - 2 // the 1pt-high rectangle was drawn at signY-2
|
||||
// raise date fields by 0.4cm (≈11.34pt) upward relative to the line
|
||||
const dateRaise = 11.34
|
||||
// For page 1 we need to move only the date input up by 5.2mm (≈14.739pt)
|
||||
const signDatumExtraRaise = 14.739
|
||||
form.createTextField('sign_datum').addToPage(page, { x: dateFieldX, y: signatureLineY - fieldHeight + signDatumExtraRaise, width: dateFieldWidth, height: fieldHeight, font: helv })
|
||||
// second signature line 3cm below first
|
||||
const threeCm = 85.04
|
||||
const secondY = signY - threeCm
|
||||
const line2Width = 300
|
||||
page.drawRectangle({ x: leftX, y: secondY - 2, width: line2Width, height: 1, color: rgb(0,0,0) })
|
||||
const label2 = 'Unterschrift (bei Jugendlichen gesetzlicher Vertreter)'
|
||||
page.drawText(label2, { x: leftX, y: secondY - 18, size: 11, font: helv })
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
// footer: right-aligned 'Seite 1 von 3' 2cm from bottom
|
||||
const footerY = 56.7
|
||||
const footerText = 'Seite 1 von 3'
|
||||
const footerSize = 10
|
||||
const footerWidth = helv.widthOfTextAtSize(footerText, footerSize)
|
||||
page.drawText(footerText, { x: width - footerWidth - leftX, y: footerY, size: footerSize, font: helv })
|
||||
|
||||
// --- Add a second page with the same header and horizontal bar ---
|
||||
const page2 = pdfDoc.addPage([595.28, 841.89])
|
||||
const { width: width2, height: height2 } = page2.getSize()
|
||||
const textWidth2 = helv.widthOfTextAtSize(headerText, headerSize)
|
||||
const headerX2 = (width2 - textWidth2) / 2
|
||||
const headerY2 = height2 - 72
|
||||
page2.drawText(headerText, { x: headerX2, y: headerY2, size: headerSize, font: helv })
|
||||
const barY2 = headerY2 - 20
|
||||
page2.drawRectangle({ x: 0, y: barY2, width: width2, height: barHeight, color: rgb(0,0,0) })
|
||||
|
||||
// --- Page 2: SEPA-Lastschriftmandat text and fields ---
|
||||
// move SEPA section slightly up so title sits closer to the header bar
|
||||
const sepaYStart = headerY2 - 54
|
||||
const sepaLeft = leftX
|
||||
// increase line gap for SEPA section (~22pt)
|
||||
const lineGap = 22
|
||||
let sy = sepaYStart
|
||||
const small = 11
|
||||
page2.drawText('Erteilung eines SEPA-Lastschriftmandates', { x: sepaLeft, y: sy, size: 12, font: helvBold })
|
||||
sy -= lineGap
|
||||
// draw these two lines as a tight block (no extra gap)
|
||||
page2.drawText('Harheimer Tischtennis-Club 1954 e.V.', { x: sepaLeft, y: sy, size: small, font: helv })
|
||||
sy -= 12
|
||||
page2.drawText('Unsere Gläubiger-Identifikationsnummer: DE46ZZZ00000745362', { x: sepaLeft, y: sy, size: small, font: helv })
|
||||
// add 0.7cm vertical gap (≈19.8pt) before the authorization paragraph
|
||||
sy -= 19.8
|
||||
// draw the authorization text as wrapped lines within page margins
|
||||
const wrapMaxWidth = width2 - sepaLeft - 48
|
||||
function drawWrapped(text, x, y, size, font) {
|
||||
const words = text.split(' ')
|
||||
let line = ''
|
||||
let curY = y
|
||||
for (const w of words) {
|
||||
const test = line ? line + ' ' + w : w
|
||||
const testWidth = helv.widthOfTextAtSize(test, size)
|
||||
if (testWidth > wrapMaxWidth) {
|
||||
page2.drawText(line, { x, y: curY, size, font })
|
||||
line = w
|
||||
curY -= size + 4
|
||||
} else {
|
||||
line = test
|
||||
}
|
||||
}
|
||||
if (line) {
|
||||
page2.drawText(line, { x, y: curY, size, font })
|
||||
curY -= size + 4
|
||||
}
|
||||
return curY
|
||||
}
|
||||
sy = drawWrapped('Hiermit ermächtige ich den Harheimer Tischtennis-Club 1954 e.V. die jährlichen Mitgliedsbeiträge von meinem untenstehenden Konto per Lastschrift einzuziehen. Zugleich weise ich mein Kreditinstitut an, die vom Harheimer Tischtennis-Club 1954 e.V. auf mein Konto gezogenen Lastschriften einzulösen.', sepaLeft, sy, small, helv)
|
||||
sy -= lineGap * 0.2
|
||||
// draw mandate reference and validity with a small gap
|
||||
page2.drawText('Mandatsreferenz: HTC0000 _ _ _', { x: sepaLeft, y: sy, size: small, font: helv })
|
||||
// add 0.7cm space after Mandatsreferenz as requested
|
||||
sy -= 19.8
|
||||
page2.drawText('Dieses Mandat gilt für die zugrundeliegende Beitrittserklärung ab sofort.', { x: sepaLeft, y: sy, size: small, font: helv })
|
||||
sy -= lineGap * 0.6
|
||||
|
||||
// Draw labeled lines and create text fields for mandate details
|
||||
const fieldHeight2 = 14
|
||||
const fieldWidth2 = 380
|
||||
const labelOffset = 0
|
||||
// place SEPA inputs 1cm left (≈28.35pt) from previous and slightly up; adjust to align with labels
|
||||
const inputX = sepaLeft + 220 - 28.35
|
||||
// reduce vertical raise so inputs sit on same baseline as labels (previously too high)
|
||||
const inputYAdjust = 6
|
||||
|
||||
// apply labelsShift only to the SEPA form labels/inputs so paragraphs remain unaffected
|
||||
let syFields = sy + labelsShift
|
||||
page2.drawText('Mitglied (Vorname und Name)', { x: sepaLeft + labelOffset, y: syFields, size: small, font: helv })
|
||||
form.createTextField('sepa_mitglied').addToPage(page2, { x: inputX, y: syFields - fieldHeight2 + 2 + inputYAdjust, width: Math.round(fieldWidth2 * fieldShrinkFactor), height: fieldHeight2, font: helv })
|
||||
syFields -= lineGap * 1.1
|
||||
|
||||
page2.drawText('Kontoinhaber (Vorname und Name):', { x: sepaLeft + labelOffset, y: syFields, size: small, font: helv })
|
||||
form.createTextField('sepa_kontoinhaber').addToPage(page2, { x: inputX, y: syFields - fieldHeight2 + 2 + inputYAdjust, width: Math.round(fieldWidth2 * fieldShrinkFactor), height: fieldHeight2, font: helv })
|
||||
syFields -= lineGap * 1.1
|
||||
|
||||
page2.drawText('Straße und Hausnummer:', { x: sepaLeft + labelOffset, y: syFields, size: small, font: helv })
|
||||
form.createTextField('sepa_strasse').addToPage(page2, { x: inputX, y: syFields - fieldHeight2 + 2 + inputYAdjust, width: Math.round(fieldWidth2 * fieldShrinkFactor), height: fieldHeight2, font: helv })
|
||||
syFields -= lineGap * 1.1
|
||||
|
||||
page2.drawText('PLZ und Ort:', { x: sepaLeft + labelOffset, y: syFields, size: small, font: helv })
|
||||
form.createTextField('sepa_plz_ort').addToPage(page2, { x: inputX, y: syFields - fieldHeight2 + 2 + inputYAdjust, width: Math.round(fieldWidth2 * fieldShrinkFactor), height: fieldHeight2, font: helv })
|
||||
syFields -= lineGap * 1.1
|
||||
|
||||
page2.drawText('Kreditinstitut:', { x: sepaLeft + labelOffset, y: syFields, size: small, font: helv })
|
||||
form.createTextField('sepa_bank').addToPage(page2, { x: inputX, y: syFields - fieldHeight2 + 2 + inputYAdjust, width: Math.round(fieldWidth2 * fieldShrinkFactor), height: fieldHeight2, font: helv })
|
||||
syFields -= lineGap * 1.1
|
||||
|
||||
page2.drawText('IBAN:', { x: sepaLeft + labelOffset, y: syFields, size: small, font: helv })
|
||||
form.createTextField('sepa_iban').addToPage(page2, { x: inputX, y: syFields - fieldHeight2 + 2 + inputYAdjust, width: Math.round(fieldWidth2 * fieldShrinkFactor), height: fieldHeight2, font: helv })
|
||||
syFields -= lineGap * 1.1
|
||||
|
||||
page2.drawText('BIC:', { x: sepaLeft + labelOffset, y: syFields, size: small, font: helv })
|
||||
// BIC remains full width as requested
|
||||
form.createTextField('sepa_bic').addToPage(page2, { x: inputX, y: syFields - fieldHeight2 + 2 + inputYAdjust, width: 220, height: fieldHeight2, font: helv })
|
||||
syFields -= lineGap * 1.1
|
||||
|
||||
// add signature and date lines 2cm below last field
|
||||
const twoCm = 56.7
|
||||
const signY2 = syFields - twoCm
|
||||
// date text and line
|
||||
const sigDateText = 'Frankfurt/Main-Harheim, den'
|
||||
page2.drawText(sigDateText + ' ', { x: sepaLeft, y: signY2, size: 12, font: helv })
|
||||
const sigDateStartX = sepaLeft + helv.widthOfTextAtSize(sigDateText + ' ', 12)
|
||||
const sigDateWidth = 160
|
||||
page2.drawRectangle({ x: sigDateStartX, y: signY2 - 2, width: sigDateWidth, height: 1, color: rgb(0,0,0) })
|
||||
page2.drawText('Datum', { x: sigDateStartX + sigDateWidth / 2 - helv.widthOfTextAtSize('Datum', 11) / 2, y: signY2 - 18, size: 11, font: helv })
|
||||
// create a date form field on page 2 centered under the date line
|
||||
const sepaDateFieldWidth = 120
|
||||
const sepaDateFieldX = sigDateStartX + sigDateWidth / 2 - sepaDateFieldWidth / 2
|
||||
// position sepa date field so its bottom edge is on the signature line
|
||||
// raise SEPA date field by the same amount so its top/bottom alignment matches requested position
|
||||
form.createTextField('sepa_datum').addToPage(page2, { x: sepaDateFieldX, y: signY2 - 2 - fieldHeight2 + dateRaise, width: sepaDateFieldWidth, height: fieldHeight2, font: helv })
|
||||
|
||||
// footer on page 2: right-aligned 'Seite 2 von 3' 2cm from bottom
|
||||
const footerText2 = 'Seite 2 von 3'
|
||||
const footerSize2 = 10
|
||||
const footerWidth2 = helv.widthOfTextAtSize(footerText2, footerSize2)
|
||||
page2.drawText(footerText2, { x: width2 - footerWidth2 - leftX, y: 56.7, size: footerSize2, font: helv })
|
||||
|
||||
// signature line
|
||||
// move signature label/line 2cm (≈56.7pt) further down
|
||||
const signLineY = signY2 - 36 - 56.7
|
||||
const signLineWidth = 300
|
||||
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')
|
||||
|
||||
// --- Add a third page with same header/bar/footer and title 'Einwilligungserklärung' ---
|
||||
const page3 = pdfDoc.addPage([595.28, 841.89])
|
||||
const { width: width3, height: height3 } = page3.getSize()
|
||||
const headerX3 = (width3 - textWidth) / 2
|
||||
const headerY3 = height3 - 72
|
||||
page3.drawText(headerText, { x: headerX3, y: headerY3, size: headerSize, font: helv })
|
||||
const barY3 = headerY3 - 20
|
||||
page3.drawRectangle({ x: 0, y: barY3, width: width3, height: barHeight, color: rgb(0,0,0) })
|
||||
|
||||
// title for page 3
|
||||
const page3Title = 'Einwilligungserklärung'
|
||||
const page3TitleSize = 14
|
||||
const page3TitleX = leftX
|
||||
const page3TitleY = headerY3 - 48
|
||||
page3.drawText(page3Title, { x: page3TitleX, y: page3TitleY, size: page3TitleSize, font: helvBold })
|
||||
|
||||
// footer on page 3
|
||||
const footerText3 = 'Seite 3 von 3'
|
||||
const footerSize3 = 10
|
||||
const footerWidth3 = helv.widthOfTextAtSize(footerText3, footerSize3)
|
||||
page3.drawText(footerText3, { x: width3 - footerWidth3 - leftX, y: 56.7, size: footerSize3, font: helv })
|
||||
}
|
||||
|
||||
create().catch(e => {
|
||||
console.error(e)
|
||||
process.exit(1)
|
||||
})
|
||||
Reference in New Issue
Block a user