Implement proper PDF parser using pdfjs-dist library
This commit is contained in:
1
package-lock.json
generated
1
package-lock.json
generated
@@ -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"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user