Files
harheimertc/scripts/fill-sample-template.js

259 lines
9.8 KiB
JavaScript

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) })