Improve Satzung content loading and HTML conversion process
Some checks failed
Code Analysis (JS/Vue) / analyze (push) Failing after 48s

This commit ensures that the Satzung content is loaded as a string, enhancing reliability. Additionally, it refines the HTML conversion function by improving the handling of line breaks, merging related lines, and removing empty paragraphs. These changes enhance the overall quality and readability of the generated HTML content.
This commit is contained in:
Torsten Schulz (local)
2026-02-06 13:35:20 +01:00
parent cc92615333
commit 04e38aa7d3
2 changed files with 133 additions and 50 deletions

View File

@@ -183,12 +183,15 @@ async function loadCurrentSatzung() {
lastUpdated.value = new Date().toLocaleDateString('de-DE') lastUpdated.value = new Date().toLocaleDateString('de-DE')
} }
if (satzung?.content) { if (satzung?.content) {
satzungContent.value = satzung.content // Stelle sicher, dass der Inhalt als String geladen wird
const content = typeof satzung.content === 'string' ? satzung.content : String(satzung.content || '')
satzungContent.value = content
} else { } else {
satzungContent.value = '' satzungContent.value = ''
} }
} catch (e) { } catch (e) {
console.error('Fehler beim Laden der aktuellen Satzung:', e) console.error('Fehler beim Laden der aktuellen Satzung:', e)
satzungContent.value = ''
} }
} }

View File

@@ -161,74 +161,154 @@ export default defineEventHandler(async (event) => {
// PDF-Text zu HTML konvertieren // PDF-Text zu HTML konvertieren
function convertTextToHtml(text) { function convertTextToHtml(text) {
// Text bereinigen und strukturieren // Text bereinigen und strukturieren
let html = text let cleaned = text
.replace(/\r\n/g, '\n') // Windows-Zeilenumbrüche normalisieren .replace(/\r\n/g, '\n') // Windows-Zeilenumbrüche normalisieren
.replace(/\r/g, '\n') // Mac-Zeilenumbrüche normalisieren .replace(/\r/g, '\n') // Mac-Zeilenumbrüche normalisieren
.replace(/\n\s*\n/g, '\n\n') // Mehrfache Zeilenumbrüche reduzieren
.trim() .trim()
// Seitenzahlen und Seitenfuß entfernen (z.B. "Seite 2 von 4", "-2-") // Seitenzahlen und Seitenfuß entfernen
html = html cleaned = cleaned
.replace(/^Seite\s+\d+\s+von\s+\d+.*$/gm, '') .replace(/^Seite\s+\d+\s+von\s+\d+.*$/gm, '')
.replace(/^-+\d+-+\s*$/gm, '') .replace(/^-+\d+-+\s*$/gm, '')
.replace(/\n\s*-+\d+-+\s*\n/g, '\n')
.replace(/\s*-+\d+-+\s*/g, '')
.replace(/zuletzt geändert am \d{2}\.\d{2}\.\d{4}.*$/gm, '')
// Überschriften erkennen und formatieren // Zeilenweise aufteilen und leere Zeilen filtern
html = html.replace(/^(Vereinssatzung|Satzung)$/gm, '<h1>$1</h1>') let rawLines = cleaned.split('\n').map(l => l.trim()).filter(l => {
html = html.replace(/^(§\s*\d+[^§\n]*)$/gm, '<h2>$1</h2>') if (!l || l.length === 0) return false
if (/^-+\d+-+$/.test(l)) return false
if (/^Seite\s+\d+\s+von\s+\d+/.test(l)) return false
return true
})
// Absätze erstellen // ============================================================
html = html.split('\n\n').map(paragraph => { // SCHRITT 1: Zusammengehörige Zeilen zusammenführen
paragraph = paragraph.trim() // pdftotext trennt oft Nummer/Prefix und Inhalt auf zwei Zeilen
if (!paragraph) return '' // ============================================================
const merged = []
for (let j = 0; j < rawLines.length; j++) {
const line = rawLines[j]
const next = j + 1 < rawLines.length ? rawLines[j + 1] : null
// Überschriften nicht als Paragraphen behandeln // Fall 1: "§ 1" (nur Paragraphennummer) + nächste Zeile ist der Titel
if (paragraph.match(/^<h[1-6]>/) || paragraph.match(/^§\s*\d+/)) { // z.B. "§ 1" + "Name, Sitz und Zweck" → "§ 1 Name, Sitz und Zweck"
return paragraph if (/^§\s*\d+\s*$/.test(line) && next && !next.match(/^§/) && !next.match(/^\d+\.\s/)) {
merged.push(line + ' ' + next)
j++ // nächste Zeile überspringen
continue
} }
// Spezielle Behandlung für Aufzählungen mit a), b), c) ... // Fall 2: "1." (nur Nummer mit Punkt) + nächste Zeile ist der Text
if (paragraph.match(/^[a-z]\)\s*$/mi)) { // z.B. "1." + "Der Harheimer TC..." → "1. Der Harheimer TC..."
const lines = paragraph.split('\n').map(l => l.trim()).filter(Boolean) if (/^\d+\.\s*$/.test(line) && next) {
const items = [] merged.push(line + ' ' + next)
let current = '' j++
continue
}
for (const line of lines) { // Fall 3: "a)" (nur Buchstabe mit Klammer) + nächste Zeile ist der Text
if (/^[a-z]\)\s*$/i.test(line)) { // z.B. "a)" + "Die Bestimmungen..." → "a) Die Bestimmungen..."
// neuer Aufzählungspunkt, vorherigen abschließen if (/^[a-z]\)\s*$/i.test(line) && next) {
if (current) items.push(current.trim()) merged.push(line + ' ' + next)
current = line j++
} else { continue
// Text zum aktuellen Aufzählungspunkt hinzufügen }
current += (current ? ' ' : '') + line
// Keine Zusammenführung nötig
merged.push(line)
}
// ============================================================
// SCHRITT 2: HTML-Elemente erzeugen
// ============================================================
const result = []
let i = 0
while (i < merged.length) {
const line = merged[i]
// Überschriften erkennen (§1, § 2, etc.)
if (line.match(/^§\s*\d+/)) {
result.push(`<h2>${line}</h2>`)
i++
continue
}
// Prüfe ob wir eine Liste mit a), b), c) haben
// Suche nach einem Muster wie "2. Text:" gefolgt von "a) ...", "b) ...", etc.
if (line.match(/^\d+\.\s+.*:$/) && i + 1 < merged.length && merged[i + 1].match(/^[a-z]\)\s+/i)) {
// Einleitender Text für die Liste (ohne Nummer)
const introText = line.replace(/^\d+\.\s+/, '')
const listItems = []
i++
// Sammle alle Listenpunkte a), b), c) ...
while (i < merged.length && merged[i].match(/^[a-z]\)\s+/i)) {
const itemText = merged[i].replace(/^[a-z]\)\s+/i, '').trim()
if (itemText) {
listItems.push(itemText)
} }
i++
} }
if (current) items.push(current.trim())
const listItems = items.map(item => { if (listItems.length > 0) {
return `<li>${item}</li>` const listHtml = listItems.map(item => `<li>${item}</li>`).join('')
}).join('') result.push(`<p><strong>${introText}</strong></p><ul>${listHtml}</ul>`)
} else {
return `<ul>${listItems}</ul>` result.push(`<p>${line}</p>`)
}
continue
} }
// Allgemeine Listen erkennen (Bullet "•", Bindestrich- oder Nummern-Listen) // Einzelne Listenpunkte a), b), c) erkennen
if (paragraph.includes('•') || paragraph.match(/^[\-•]\s/m) || paragraph.match(/^\d+\.\s/m)) { if (line.match(/^[a-z]\)\s+/i)) {
const listItems = paragraph.split(/\n/).map(item => { const items = []
item = item.trim() while (i < merged.length && merged[i].match(/^[a-z]\)\s+/i)) {
if (item.match(/^[•-]\s/) || item.match(/^\d+\.\s/)) { const itemText = merged[i].replace(/^[a-z]\)\s+/i, '').trim()
return `<li>${item.replace(/^[•-]\s/, '').replace(/^\d+\.\s/, '')}</li>` if (itemText) {
items.push(itemText)
} }
return item ? `<li>${item}</li>` : '' i++
}).filter(Boolean).join('') }
return `<ul>${listItems}</ul>` if (items.length > 0) {
const listHtml = items.map(item => `<li>${item}</li>`).join('')
result.push(`<ul>${listHtml}</ul>`)
}
continue
}
// Nummerierte Listen (1., 2., 3.) - aber nur wenn mehrere aufeinander folgen
if (line.match(/^\d+\.\s+/) && i + 1 < merged.length && merged[i + 1].match(/^\d+\.\s+/)) {
const items = []
while (i < merged.length && merged[i].match(/^\d+\.\s+/)) {
const itemText = merged[i].replace(/^\d+\.\s+/, '').trim()
// Prüfe ob es eine Einleitung für eine Unterliste ist (endet mit ":")
if (itemText.endsWith(':') && i + 1 < merged.length && merged[i + 1].match(/^[a-z]\)\s+/i)) {
break // Wird oben als Einleitung + Unterliste behandelt
}
if (itemText) {
items.push(itemText)
}
i++
}
if (items.length > 0) {
const listHtml = items.map(item => `<li>${item}</li>`).join('')
result.push(`<ol>${listHtml}</ol>`)
}
continue
} }
// Normale Absätze // Normale Absätze
return `<p>${paragraph.replace(/\n/g, '<br>')}</p>` result.push(`<p>${line}</p>`)
}).join('\n') i++
}
// Mehrfache Zeilenumbrüche entfernen let html = result.join('\n')
html = html.replace(/\n{3,}/g, '\n\n')
return html // Leere Absätze entfernen
html = html.replace(/<p>\s*<\/p>/g, '')
html = html.replace(/<p><\/p>/g, '')
return html.trim()
} }