Implement proper PDF parser using pdfjs-dist library

This commit is contained in:
Torsten Schulz (local)
2025-10-22 14:30:24 +02:00
parent 22e3dc3497
commit 57db75e48e
3 changed files with 66 additions and 33 deletions

1
package-lock.json generated
View File

@@ -16,6 +16,7 @@
"nodemailer": "^7.0.9", "nodemailer": "^7.0.9",
"nuxt": "^4.1.3", "nuxt": "^4.1.3",
"pdf-parse": "^2.4.5", "pdf-parse": "^2.4.5",
"pdfjs-dist": "^5.4.296",
"pinia": "^3.0.3", "pinia": "^3.0.3",
"vue": "^3.5.22" "vue": "^3.5.22"
}, },

View File

@@ -20,6 +20,7 @@
"nodemailer": "^7.0.9", "nodemailer": "^7.0.9",
"nuxt": "^4.1.3", "nuxt": "^4.1.3",
"pdf-parse": "^2.4.5", "pdf-parse": "^2.4.5",
"pdfjs-dist": "^5.4.296",
"pinia": "^3.0.3", "pinia": "^3.0.3",
"vue": "^3.5.22" "vue": "^3.5.22"
}, },

View File

@@ -1,6 +1,10 @@
import multer from 'multer' import multer from 'multer'
import fs from 'fs/promises' import fs from 'fs/promises'
import path from 'path' import path from 'path'
import * as pdfjsLib from 'pdfjs-dist'
// PDF.js Worker konfigurieren
pdfjsLib.GlobalWorkerOptions.workerSrc = 'pdfjs-dist/build/pdf.worker.min.js'
// Handle both dev and production paths // Handle both dev and production paths
const getDataPath = (filename) => { const getDataPath = (filename) => {
@@ -64,25 +68,22 @@ export default defineEventHandler(async (event) => {
}) })
} }
// Für jetzt: Satzungsinhalt als Platzhalter bis PDF-Parsing implementiert ist // PDF-Text extrahieren mit PDF.js
const htmlContent = ` const pdfBuffer = await fs.readFile(file.path)
<h2>§ 1 Name und Sitz</h2> const pdfData = await pdfjsLib.getDocument({ data: pdfBuffer }).promise
<p>Der Verein führt den Namen "Harheimer Tischtennis-Club" und hat seinen Sitz in Harheim.</p>
<h2>§ 2 Zweck des Vereins</h2> let fullText = ''
<p>Der Verein verfolgt ausschließlich und unmittelbar gemeinnützige Zwecke im Sinne des Abschnitts "Steuerbegünstigte Zwecke" der Abgabenordnung.</p>
<h2>§ 3 Mitgliedschaft</h2> // Alle Seiten durchgehen
<p>Mitglied des Vereins kann jede natürliche Person werden, die die Ziele des Vereins unterstützt.</p> for (let pageNum = 1; pageNum <= pdfData.numPages; pageNum++) {
const page = await pdfData.getPage(pageNum)
const textContent = await page.getTextContent()
const pageText = textContent.items.map(item => item.str).join(' ')
fullText += pageText + '\n'
}
<h2>§ 4 Beiträge</h2> // Text in HTML-Format konvertieren
<p>Die Mitglieder zahlen Beiträge nach Maßgabe der Beitragsordnung.</p> const htmlContent = convertTextToHtml(fullText)
<h2>§ 5 Vorstand</h2>
<p>Der Vorstand besteht aus dem Vorsitzenden, dem stellvertretenden Vorsitzenden, dem Kassenwart und dem Schriftführer.</p>
<p><em>Hinweis: Dies ist ein Platzhalter-Inhalt. Der vollständige Satzungstext wird automatisch aus der hochgeladenen PDF-Datei extrahiert, sobald das PDF-Parsing implementiert ist.</em></p>
`
// Config aktualisieren // Config aktualisieren
const configPath = getDataPath('config.json') const configPath = getDataPath('config.json')
@@ -110,17 +111,47 @@ export default defineEventHandler(async (event) => {
} }
}) })
// TODO: PDF-Parsing-Funktion später implementieren // PDF-Text zu HTML konvertieren
// function convertTextToHtml(text) { function convertTextToHtml(text) {
// // Einfache Text-zu-HTML-Konvertierung // Text bereinigen und strukturieren
// let html = text let html = text
// .replace(/\n\n+/g, '</p><p>') // Absätze .replace(/\r\n/g, '\n') // Windows-Zeilenumbrüche normalisieren
// .replace(/\n/g, '<br>') // Zeilenumbrüche .replace(/\r/g, '\n') // Mac-Zeilenumbrüche normalisieren
// .replace(/^(.+)$/gm, '<p>$1</p>') // Alle Zeilen in Paragraphen .replace(/\n\s*\n/g, '\n\n') // Mehrfache Zeilenumbrüche reduzieren
// .trim()
// // Überschriften erkennen (einfache Heuristik)
// html = html.replace(/<p>(§\s*\d+.*?)<\/p>/g, '<h3>$1</h3>') // Überschriften erkennen und formatieren
// html = html.replace(/<p>(\d+\.\s+.*?)<\/p>/g, '<h4>$1</h4>') html = html.replace(/^(Vereinssatzung|Satzung)$/gm, '<h1>$1</h1>')
// html = html.replace(/^(§\s*\d+[^§\n]*)$/gm, '<h2>$1</h2>')
// return `<h2>Satzung</h2>${html}`
// } // Absätze erstellen
html = html.split('\n\n').map(paragraph => {
paragraph = paragraph.trim()
if (!paragraph) return ''
// Überschriften nicht als Paragraphen behandeln
if (paragraph.match(/^<h[1-6]>/) || paragraph.match(/^§\s*\d+/)) {
return paragraph
}
// Listen erkennen
if (paragraph.includes('•') || paragraph.includes('-') || paragraph.match(/^\d+\./)) {
const listItems = paragraph.split(/\n/).map(item => {
item = item.trim()
if (item.match(/^[•\-]\s/) || item.match(/^\d+\.\s/)) {
return `<li>${item.replace(/^[•\-]\s/, '').replace(/^\d+\.\s/, '')}</li>`
}
return `<li>${item}</li>`
}).join('')
return `<ul>${listItems}</ul>`
}
// Normale Absätze
return `<p>${paragraph.replace(/\n/g, '<br>')}</p>`
}).join('\n')
// Mehrfache Zeilenumbrüche entfernen
html = html.replace(/\n{3,}/g, '\n\n')
return html
}