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() // Ensure a readable font is embedded and used for field appearances const helv = await pdfDoc.embedFont(StandardFonts.Helvetica) // 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 } } // Robust setter: find a field by name (case-insensitive) and set text/checkbox/select accordingly function setFieldByName(name, value) { try { const lower = name.toLowerCase() const field = form.getFields().find(f => f.getName() && f.getName().toLowerCase() === lower) if (!field) { console.log(`DEBUG: Field not found for '${name}'`) return false } // Text field if (typeof field.setText === 'function') { field.setText(value == null ? '' : String(value)) return true } // Check box if (typeof field.check === 'function') { if (value === true || String(value).toLowerCase() === 'true') field.check() else if (typeof field.uncheck === 'function') field.uncheck() return true } // Radio/select (pdf-lib uses select for dropdowns) if (typeof field.select === 'function') { try { field.select(String(value)) } catch (e) { /* ignore */ } return true } console.log(`DEBUG: Unsupported field type for '${name}'`) return false } catch (e) { console.log(`DEBUG: Error setting field '${name}':`, e.message) return false } } // Debug: list all form fields found in the template try { const allFields = form.getFields().map(f => f.getName()) console.log('DEBUG: Template form fields:', allFields.join(', ')) } catch (e) { console.log('DEBUG: Could not list form fields:', e.message) } setFieldByName('nachname', sample.nachname) setFieldByName('vorname', sample.vorname) setFieldByName('strasse', sample.strasse) setFieldByName('plz_ort', sample.plz_ort) setFieldByName('geburtsdatum', sample.geburtsdatum) setFieldByName('telefon', sample.telefon) setFieldByName('email', sample.email) setFieldByName('telefon_mobil', sample.telefon_mobil) // Checkboxes via robust setter setFieldByName('mitglied_aktiv', sample.mitglied_aktiv) setFieldByName('mitglied_passiv', sample.mitglied_passiv) setFieldByName('sepa_mitglied', sample.sepa_mitglied) setFieldByName('sepa_kontoinhaber', sample.sepa_kontoinhaber) setFieldByName('sepa_strasse', sample.sepa_strasse) setFieldByName('sepa_plz_ort', sample.sepa_plz_ort) setFieldByName('sepa_bank', sample.sepa_bank) setFieldByName('sepa_iban', sample.sepa_iban) setFieldByName('sepa_bic', sample.sepa_bic) setFieldByName('sepa_datum', sample.sepa_datum) setFieldByName('sign_datum', sample.sign_datum) // page3 fields setFieldByName('page3_name', sample.page3_name) setFieldByName('page3_vorname', sample.page3_vorname) setFieldByName('page3_anschrift', sample.page3_anschrift) setFieldByName('page3_telefon', sample.page3_telefon) setFieldByName('page3_fax', sample.page3_fax) setFieldByName('page3_email', sample.page3_email) setFieldByName('page3_datum', sample.page3_datum) // Debug: check which sample keys correspond to actual fields try { const names = form.getFields().map(f => f.getName().toLowerCase()) for (const key of Object.keys(sample)) { const found = names.includes(key.toLowerCase()) console.log(`DEBUG: sample key='${key}' -> field present=${found}`) } } catch (e) { console.log('DEBUG: field presence check failed:', e.message) } // Debug: read back all field values after setting (before flattening) try { console.log('DEBUG: Field values after setting:') for (const f of form.getFields()) { const name = f.getName() let val = null try { if (typeof f.getText === 'function') val = f.getText() else if (typeof f.isChecked === 'function') val = f.isChecked() else val = '(no getter)' } catch (e) { val = `(error reading: ${e.message})` } console.log(` ${name}: ${val}`) } } catch (e) { console.log('DEBUG: Could not read back field values:', e.message) } // Debug: print widget rectangles for relevant fields (SEPA and page3) try { const interesting = ['sepa_mitglied','sepa_kontoinhaber','sepa_strasse','sepa_plz_ort','sepa_bank','sepa_iban','sepa_bic','page3_name','page3_vorname','page3_anschrift','page3_telefon','page3_email'] for (const fname of interesting) { const f = form.getFields().find(x => x.getName && x.getName().toLowerCase() === fname) if (!f) { console.log(`DEBUG: no field object for ${fname}`); continue } try { // attempt to access widget rectangle via low-level acroField const acro = f.acroField const widgets = acro.getWidgets() if (!widgets || widgets.length === 0) { console.log(`DEBUG: no widgets for ${fname}`); continue } const rect = widgets[0].getRectangle() console.log(`DEBUG: widget rect for ${fname}: ${JSON.stringify(rect)}`) } catch (e) { console.log(`DEBUG: cannot read widget rect for ${fname}: ${e.message}`) } } } catch (e) { console.log('DEBUG: widget rect inspection failed:', e.message) } // Define fallback drawing: draw visible text directly onto pages at widget rect positions async function fallbackDraw() { try { const pages = pdfDoc.getPages() // draw SEPA fields on page 2 (index 1) const p2 = pages[1] const sepaFields = ['sepa_mitglied','sepa_kontoinhaber','sepa_strasse','sepa_plz_ort','sepa_bank','sepa_iban','sepa_bic'] for (const fname of sepaFields) { const f = form.getFields().find(x => x.getName && x.getName().toLowerCase() === fname) if (!f) continue try { const widgets = f.acroField.getWidgets() if (!widgets || widgets.length === 0) continue const rect = widgets[0].getRectangle() const text = (typeof f.getText === 'function') ? f.getText() : '' if (text) { p2.drawText(String(text), { x: rect.x + 2, y: rect.y + rect.height - 12, size: 11, font: helv }) console.log(`FALLBACK: drew ${fname} on page2 at ${rect.x},${rect.y}`) } } catch (e) { console.log(`FALLBACK: could not draw ${fname}: ${e.message}`) } } // draw page3 fields on page 3 (index 2) const p3 = pages[2] const p3Fields = ['page3_name','page3_vorname','page3_anschrift','page3_telefon','page3_email'] for (const fname of p3Fields) { const f = form.getFields().find(x => x.getName && x.getName().toLowerCase() === fname) if (!f) continue try { const widgets = f.acroField.getWidgets() if (!widgets || widgets.length === 0) continue const rect = widgets[0].getRectangle() const text = (typeof f.getText === 'function') ? f.getText() : '' if (text) { p3.drawText(String(text), { x: rect.x + 2, y: rect.y + rect.height - 12, size: 11, font: helv }) console.log(`FALLBACK: drew ${fname} on page3 at ${rect.x},${rect.y}`) } } catch (e) { console.log(`FALLBACK: could not draw ${fname}: ${e.message}`) } } // write fallback copy const fallbackBytes = await pdfDoc.save() if (!fs.existsSync('temp')) fs.mkdirSync('temp') fs.writeFileSync('temp/mitgliedschaft-sample-filled-fallback.pdf', fallbackBytes) console.log('Wrote temp/mitgliedschaft-sample-filled-fallback.pdf') } catch (e) { console.log('FALLBACK drawing failed:', e.message) } } // Update field appearances so text is visible, then flatten. Run fallback only when enabled. try { form.updateFieldAppearances(helv) const outUnflattened = await pdfDoc.save() if (!fs.existsSync('temp')) fs.mkdirSync('temp') fs.writeFileSync('temp/mitgliedschaft-sample-filled-unflattened.pdf', outUnflattened) form.flatten() } catch (e) { console.warn('Could not update field appearances:', e.message) const enableFallback = process.env.ENABLE_FALLBACK === '1' || (typeof sample.debug !== 'undefined' && sample.debug) if (enableFallback) { try { await fallbackDraw() } catch (err) { console.warn('Fallback draw failed:', err.message) } try { form.flatten() } catch (err) { /* ignore */ } } } 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) })