diff --git a/scripts/create-fillable-template.js b/scripts/create-fillable-template.js index 9d95fff..8539b19 100644 --- a/scripts/create-fillable-template.js +++ b/scripts/create-fillable-template.js @@ -258,39 +258,42 @@ async function create() { 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 + const inputX = sepaLeft + 220 - 28.35 + // reduce vertical raise so inputs sit on same baseline as labels (previously too high) + const inputYAdjust = 6 + 5.67 // Move SEPA inputs (except date) up by additional 2mm (≈5.67pt) + // adjust page2DownShift so inputs move up by 0.75mm (net) + // 0.75mm = 0.075cm -> ~2.12625 pt + const page2DownShift = 0.075 * 28.35 // 0.075 cm -> ~2.12625 pt // 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 }) + form.createTextField('sepa_mitglied').addToPage(page2, { x: inputX, y: syFields - fieldHeight2 + 2 + inputYAdjust - page2DownShift, 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 }) + form.createTextField('sepa_kontoinhaber').addToPage(page2, { x: inputX, y: syFields - fieldHeight2 + 2 + inputYAdjust - page2DownShift, 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 }) + form.createTextField('sepa_strasse').addToPage(page2, { x: inputX, y: syFields - fieldHeight2 + 2 + inputYAdjust - page2DownShift, 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 }) + form.createTextField('sepa_plz_ort').addToPage(page2, { x: inputX, y: syFields - fieldHeight2 + 2 + inputYAdjust - page2DownShift, 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 }) + form.createTextField('sepa_bank').addToPage(page2, { x: inputX, y: syFields - fieldHeight2 + 2 + inputYAdjust - page2DownShift, 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 }) + form.createTextField('sepa_iban').addToPage(page2, { x: inputX, y: syFields - fieldHeight2 + 2 + inputYAdjust - page2DownShift, 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 }) + form.createTextField('sepa_bic').addToPage(page2, { x: inputX, y: syFields - fieldHeight2 + 2 + inputYAdjust - page2DownShift, width: 220, height: fieldHeight2, font: helv }) syFields -= lineGap * 1.1 // add signature and date lines 2cm below last field @@ -444,6 +447,66 @@ Das Vereinsmitglied trifft die Entscheidung zur Veröffentlichung seiner Daten i py = drawWrappedOnPage3('folgende allgemeine Daten zu meiner Person:', leftX, py, normalSize, helv) py -= blockSpacing + // Rebuild a simple, single-column, non-overlapping form area on page 3 + const formLeft = leftX + const formFieldW = 360 + const formFieldH = 14 + const formGap = 22 + // start a bit lower to separate from the block text + py -= 28 + // Row layout + const colGap = 220 + // Shifts (in points): 1 cm = ~28.35 pt + const leftColShift = 0.3 * 28.35 // 0.3 cm -> ~8.505 pt + const vornameLabelShift = 1.0 * 28.35 // 1.0 cm -> ~28.35 pt + const vornameFieldShift = 1.3 * 28.35 // 1.3 cm -> ~36.855 pt + const leftColX = formLeft + leftColShift + const rightColX = formLeft + colGap + + // Row 1: Name (left) and Vorname (right) + // Keep the left label at the original formLeft, move only the input field slightly right by 0.1cm + const leftInputShift = 0.35 * 28.35 // 0.35 cm -> ~9.9225 pt + // extra small horizontal nudge for all non-date inputs: 1mm + const extraHShift = 0.1 * 28.35 // 0.1 cm (1 mm) -> ~2.835 pt + // vertical shift for all input fields (including date): 0.6 cm up + const verticalFieldShift = 0.6 * 28.35 // 0.6 cm -> ~17.01 pt + // downward nudge for non-date inputs requested now: 0.2 cm -> ~5.67 pt + const nonDateDownShift = 0.2 * 28.35 // 0.2 cm -> ~5.67 pt + page3.drawText('Name:', { x: formLeft, y: py, size: normalSize, font: helv }) + form.createTextField('page3_name').addToPage(page3, { x: formLeft + 70 + leftInputShift + extraHShift, y: py - formFieldH + verticalFieldShift - nonDateDownShift, width: 160, height: formFieldH, font: helv }) + // Vorname label and field moved further right as requested + page3.drawText('Vorname:', { x: rightColX + vornameLabelShift, y: py, size: normalSize, font: helv }) + form.createTextField('page3_vorname').addToPage(page3, { x: rightColX + 70 + vornameFieldShift + extraHShift, y: py - formFieldH + verticalFieldShift - nonDateDownShift, width: 160, height: formFieldH, font: helv }) + py -= formGap + // Anschrift (full width) + page3.drawText('Anschrift:', { x: formLeft, y: py, size: normalSize, font: helv }) + form.createTextField('page3_anschrift').addToPage(page3, { x: formLeft + 70 + leftInputShift + extraHShift, y: py - formFieldH + verticalFieldShift - nonDateDownShift, width: formFieldW + 40, height: formFieldH, font: helv }) + py -= formGap + // Row 3: Telefonnummer (left) and E-Mail (right) + // Keep left label at formLeft, shift only the input field by leftInputShift + page3.drawText('Telefonnummer:', { x: formLeft, y: py, size: normalSize, font: helv }) + form.createTextField('page3_telefon').addToPage(page3, { x: formLeft + 70 + leftInputShift + extraHShift, y: py - formFieldH + verticalFieldShift - nonDateDownShift, width: 160, height: formFieldH, font: helv }) + page3.drawText('E-Mail-Adresse:', { x: rightColX + vornameLabelShift, y: py, size: normalSize, font: helv }) + form.createTextField('page3_email').addToPage(page3, { x: rightColX + 70 + vornameFieldShift + extraHShift, y: py - formFieldH + verticalFieldShift - nonDateDownShift, width: 180, height: formFieldH, font: helv }) + py -= formGap + // remove fax field/label per request (space preserved) + py -= formGap + py -= formGap + // Date and signature line + const dateFieldW = 120 + page3.drawText('Frankfurt/Main-Harheim, den', { x: formLeft, y: py, size: normalSize, font: helv }) + const dateX = formLeft + helv.widthOfTextAtSize('Frankfurt/Main-Harheim, den ', normalSize) + // date field also moves up by verticalFieldShift (but not horizontally shifted by extraHShift) + // now move the date and signature line 0.2cm down as requested + const dateFieldY = py - formFieldH + verticalFieldShift - nonDateDownShift + form.createTextField('page3_datum').addToPage(page3, { x: dateX, y: dateFieldY, width: dateFieldW, height: formFieldH, font: helv }) + // signature line starts directly under the (moved) date field + page3.drawRectangle({ x: dateX, y: dateFieldY - 6, width: 300, height: 1, color: rgb(0,0,0) }) + // label under signature line + const sigLabel = 'Datum, Unterschrift (bei Jugendlichen gesetzlicher Vertreter)' + const sigLabelSize = 10 + page3.drawText(sigLabel, { x: dateX, y: dateFieldY - 18, size: sigLabelSize, font: helv }) + const pdfBytes = await pdfDoc.save() fs.writeFileSync('server/templates/mitgliedschaft-fillable.pdf', pdfBytes) console.log('Wrote server/templates/mitgliedschaft-fillable.pdf') diff --git a/scripts/fill-sample-template.js b/scripts/fill-sample-template.js new file mode 100644 index 0000000..f0239b0 --- /dev/null +++ b/scripts/fill-sample-template.js @@ -0,0 +1,100 @@ +import fs from 'fs' +import { PDFDocument, StandardFonts, rgb } from 'pdf-lib' + +async function fill() { + const templatePath = 'server/templates/mitgliedschaft-fillable.pdf' + if (!fs.existsSync(templatePath)) { + console.error('Template not found:', templatePath) + process.exit(1) + } + + const existingPdfBytes = fs.readFileSync(templatePath) + const pdfDoc = await PDFDocument.load(existingPdfBytes) + const form = pdfDoc.getForm() + + // Simple sample data + const sample = { + nachname: 'Müller', + vorname: 'Anna', + strasse: 'Hauptstr. 12', + plz_ort: '60389 Frankfurt', + geburtsdatum: '01.01.1990', + telefon: '069 123456', + email: 'anna.mueller@example.de', + telefon_mobil: '0151 2345678', + mitglied_aktiv: true, + mitglied_passiv: false, + sepa_mitglied: 'Anna Müller', + sepa_kontoinhaber: 'Anna Müller', + sepa_strasse: 'Hauptstr. 12', + sepa_plz_ort: '60389 Frankfurt', + sepa_bank: 'Sparkasse', + sepa_iban: 'DE00123456781234567890', + sepa_bic: 'PBNKDEFF', + sepa_datum: '23.10.2025', + sign_datum: '23.10.2025', + page3_name: 'Müller', + page3_vorname: 'Anna', + page3_anschrift: 'Hauptstr. 12, 60389 Frankfurt', + page3_telefon: '069 123456', + page3_fax: '069 654321', + page3_email: 'anna.mueller@example.de', + page3_datum: '23.10.2025' + } + + function safeSetText(name, value) { + try { + const f = form.getTextField(name) + f.setText(value) + } catch (e) { + // ignore missing fields + } + } + + safeSetText('nachname', sample.nachname) + safeSetText('vorname', sample.vorname) + safeSetText('strasse', sample.strasse) + safeSetText('plz_ort', sample.plz_ort) + safeSetText('geburtsdatum', sample.geburtsdatum) + safeSetText('telefon', sample.telefon) + safeSetText('email', sample.email) + safeSetText('telefon_mobil', sample.telefon_mobil) + + try { + const cbA = form.getCheckBox('mitglied_aktiv') + if (sample.mitglied_aktiv) cbA.check(); else cbA.uncheck() + } catch(e) {} + try { + const cbP = form.getCheckBox('mitglied_passiv') + if (sample.mitglied_passiv) cbP.check(); else cbP.uncheck() + } catch(e) {} + + safeSetText('sepa_mitglied', sample.sepa_mitglied) + safeSetText('sepa_kontoinhaber', sample.sepa_kontoinhaber) + safeSetText('sepa_strasse', sample.sepa_strasse) + safeSetText('sepa_plz_ort', sample.sepa_plz_ort) + safeSetText('sepa_bank', sample.sepa_bank) + safeSetText('sepa_iban', sample.sepa_iban) + safeSetText('sepa_bic', sample.sepa_bic) + safeSetText('sepa_datum', sample.sepa_datum) + safeSetText('sign_datum', sample.sign_datum) + + // page3 fields + safeSetText('page3_name', sample.page3_name) + safeSetText('page3_vorname', sample.page3_vorname) + safeSetText('page3_anschrift', sample.page3_anschrift) + safeSetText('page3_telefon', sample.page3_telefon) + safeSetText('page3_fax', sample.page3_fax) + safeSetText('page3_email', sample.page3_email) + safeSetText('page3_datum', sample.page3_datum) + + // flatten all fields + try { form.flatten() } catch (e) {} + + const out = await pdfDoc.save() + if (!fs.existsSync('temp')) fs.mkdirSync('temp') + fs.writeFileSync('temp/mitgliedschaft-sample-filled.pdf', out) + console.log('Wrote temp/mitgliedschaft-sample-filled.pdf') +} + +fill().catch(e => { console.error(e); process.exit(1) }) diff --git a/server/templates/mitgliedschaft-fillable.pdf b/server/templates/mitgliedschaft-fillable.pdf index f9021d7..2c9bd89 100644 Binary files a/server/templates/mitgliedschaft-fillable.pdf and b/server/templates/mitgliedschaft-fillable.pdf differ diff --git a/temp/mitgliedschaft-sample-filled.pdf b/temp/mitgliedschaft-sample-filled.pdf new file mode 100644 index 0000000..5e74144 Binary files /dev/null and b/temp/mitgliedschaft-sample-filled.pdf differ