diff --git a/package.json b/package.json index 2bd303e..d4ced3e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "harheimertc-website", - "version": "1.1.6", + "version": "1.2.0", "description": "Moderne Webseite für den Harheimer Tischtennis Club", "private": true, "type": "module", diff --git a/public/documents/beitrittserklärung_template.pdf b/public/documents/beitrittserklärung_template.pdf index 1047192..c4f1216 100644 Binary files a/public/documents/beitrittserklärung_template.pdf and b/public/documents/beitrittserklärung_template.pdf differ diff --git a/scripts/create-fillable-template.js b/scripts/create-fillable-template.js index 0b79573..6f0dc6e 100644 --- a/scripts/create-fillable-template.js +++ b/scripts/create-fillable-template.js @@ -74,20 +74,53 @@ async function create() { // Create form fields const form = pdfDoc.getForm() + const createUnderlinedTextField = (name, targetPage, options) => { + const { + x, + y, + width: fieldW, + height: fieldH, + font, + drawUnderline = true, + lineOffset = 1, + lineHeight = 0.7 + } = options + + if (drawUnderline) { + targetPage.drawRectangle({ + x, + y: y + lineOffset, + width: fieldW, + height: lineHeight, + color: rgb(0, 0, 0) + }) + } + + return form.createTextField(name).addToPage(targetPage, { + x, + y, + width: fieldW, + height: fieldH, + font, + borderWidth: 0, + borderColor: undefined, + backgroundColor: undefined + }) + } // 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 }) + createUnderlinedTextField('nachname', page, { x: leftX + fieldXOffset, y: baseY - fieldHeight + labelToFieldYDelta + lift + fieldsShift + labelsShift + inputFieldRaise, width: Math.round(fieldWidth * fieldShrinkFactor), height: fieldHeight, font: helv }) + createUnderlinedTextField('vorname', page, { x: rightX + fieldXOffset, y: baseY - fieldHeight + labelToFieldYDelta + lift + fieldsShift + labelsShift + inputFieldRaise, width: Math.round(fieldWidth * fieldShrinkFactor), height: fieldHeight, font: helv }) + createUnderlinedTextField('strasse', page, { x: leftX + fieldXOffset, y: baseY - gap - fieldHeight + labelToFieldYDelta + lift + fieldsShift + labelsShift + inputFieldRaise, width: Math.round(fieldWidth * fieldShrinkFactor), height: fieldHeight, font: helv }) + createUnderlinedTextField('plz_ort', page, { x: rightX + fieldXOffset, y: baseY - gap - fieldHeight + labelToFieldYDelta + lift + fieldsShift + labelsShift + inputFieldRaise, width: Math.round(fieldWidth * fieldShrinkFactor), height: fieldHeight, font: helv }) + createUnderlinedTextField('geburtsdatum', page, { x: leftX + fieldXOffset, y: baseY - gap * 2 - fieldHeight + labelToFieldYDelta + lift + fieldsShift + labelsShift + inputFieldRaise, width: Math.round(fieldWidth * fieldShrinkFactor), height: fieldHeight, font: helv }) + createUnderlinedTextField('telefon', page, { x: rightX + fieldXOffset, y: baseY - gap * 2 - fieldHeight + labelToFieldYDelta + lift + fieldsShift + labelsShift + inputFieldRaise, width: Math.round(fieldWidth * fieldShrinkFactor), height: fieldHeight, font: helv }) + createUnderlinedTextField('email', page, { x: leftX + fieldXOffset, y: baseY - gap * 3 - fieldHeight + labelToFieldYDelta + lift + fieldsShift + labelsShift + inputFieldRaise, width: Math.round(fieldWidth * fieldShrinkFactor), height: fieldHeight, font: helv }) + createUnderlinedTextField('telefon_mobil', 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 @@ -176,7 +209,7 @@ async function create() { 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 }) + createUnderlinedTextField('sign_datum', page, { x: dateFieldX, y: signatureLineY - fieldHeight + signDatumExtraRaise, width: dateFieldWidth, height: fieldHeight, font: helv, drawUnderline: false }) // second signature line 3cm below first const threeCm = 85.04 const secondY = signY - threeCm @@ -268,32 +301,32 @@ async function create() { // 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 - page2DownShift, width: Math.round(fieldWidth2 * fieldShrinkFactor), height: fieldHeight2, font: helv }) + createUnderlinedTextField('sepa_mitglied', 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 - page2DownShift, width: Math.round(fieldWidth2 * fieldShrinkFactor), height: fieldHeight2, font: helv }) + createUnderlinedTextField('sepa_kontoinhaber', 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 - page2DownShift, width: Math.round(fieldWidth2 * fieldShrinkFactor), height: fieldHeight2, font: helv }) + createUnderlinedTextField('sepa_strasse', 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 - page2DownShift, width: Math.round(fieldWidth2 * fieldShrinkFactor), height: fieldHeight2, font: helv }) + createUnderlinedTextField('sepa_plz_ort', 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 - page2DownShift, width: Math.round(fieldWidth2 * fieldShrinkFactor), height: fieldHeight2, font: helv }) + createUnderlinedTextField('sepa_bank', 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 - page2DownShift, width: Math.round(fieldWidth2 * fieldShrinkFactor), height: fieldHeight2, font: helv }) + createUnderlinedTextField('sepa_iban', 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 - page2DownShift, width: 220, height: fieldHeight2, font: helv }) + createUnderlinedTextField('sepa_bic', 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 @@ -311,7 +344,7 @@ async function create() { 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 }) + createUnderlinedTextField('sepa_datum', page2, { x: sepaDateFieldX, y: signY2 - 2 - fieldHeight2 + dateRaise, width: sepaDateFieldWidth, height: fieldHeight2, font: helv, drawUnderline: false }) // footer on page 2: right-aligned 'Seite 2 von 3' 2cm from bottom const footerText2 = 'Seite 2 von 3' @@ -473,21 +506,21 @@ Das Vereinsmitglied trifft die Entscheidung zur Veröffentlichung seiner Daten i // 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 }) + createUnderlinedTextField('page3_name', 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 }) + createUnderlinedTextField('page3_vorname', 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 }) + createUnderlinedTextField('page3_anschrift', 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 }) + createUnderlinedTextField('page3_telefon', 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 }) + createUnderlinedTextField('page3_email', 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 @@ -499,7 +532,7 @@ Das Vereinsmitglied trifft die Entscheidung zur Veröffentlichung seiner Daten i // 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 }) + createUnderlinedTextField('page3_datum', page3, { x: dateX, y: dateFieldY, width: dateFieldW, height: formFieldH, font: helv, drawUnderline: false }) // 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 @@ -515,8 +548,12 @@ Das Vereinsmitglied trifft die Entscheidung zur Veröffentlichung seiner Daten i } const pdfBytes = await pdfDoc.save() + fs.mkdirSync('server/templates', { recursive: true }) + fs.mkdirSync('public/documents', { recursive: true }) fs.writeFileSync('server/templates/mitgliedschaft-fillable.pdf', pdfBytes) + fs.writeFileSync('public/documents/beitrittserklärung_template.pdf', pdfBytes) console.log('Wrote server/templates/mitgliedschaft-fillable.pdf') + console.log('Wrote public/documents/beitrittserklärung_template.pdf') } diff --git a/server/templates/mitgliedschaft-fillable.pdf b/server/templates/mitgliedschaft-fillable.pdf index f6ff814..c4f1216 100644 Binary files a/server/templates/mitgliedschaft-fillable.pdf and b/server/templates/mitgliedschaft-fillable.pdf differ