# PROJ-35: OCR & Anhang-Volltext-Indexierung ## Status: Planned **Created:** 2026-04-04 **Last Updated:** 2026-04-04 ## Dependencies - Requires: PROJ-5 (Speicherung & Indexierung) — Mailparser + Manticore-Index - Requires: PROJ-30 (Manticore Migration) — Volltext-Index als Basis ## Motivation PDF- und Bild-Anhänge (Rechnungen, Verträge, eingescannte Dokumente) sind bisher nicht durchsuchbar — nur der Dateiname wird indexiert. OCR macht den Inhalt dieser Anhänge volltext-durchsuchbar ohne den normalen Mail-Eingang zu verlangsamen. ## User Stories - Als Nutzer möchte ich in archivierten Rechnungs-PDFs nach Beträgen oder Kundennummern suchen können. - Als Nutzer möchte ich eingescannte Dokumente (JPG/PNG/TIFF) im Volltext durchsuchen können. - Als Admin möchte ich OCR per Mandant aktivieren oder deaktivieren können. - Als Nutzer soll der Mail-Eingang nicht durch OCR verlangsamt werden (asynchrone Verarbeitung). ## Acceptance Criteria - [ ] PDF-Anhänge mit eingebettetem Text: Text via `pdftotext` extrahieren (kein OCR nötig, schnell) - [ ] PDF-Anhänge ohne Text (Scan): OCR via Tesseract (deutsch + englisch) - [ ] Bild-Anhänge (JPG, PNG, TIFF): OCR via Tesseract - [ ] OCR läuft asynchron — Mail wird sofort gespeichert, OCR-Text nachgeliefert - [ ] OCR-Text wird in Manticore-Index im Feld `attachment_text` gespeichert (neues Feld) - [ ] Volltext-Suche findet Mails anhand von OCR-Text in Anhängen - [ ] OCR-Status pro Mail in PostgreSQL: `ocr_status` (pending / done / failed / skipped) - [ ] OCR kann per Mandant deaktiviert werden (`ocr_enabled` in tenants-Tabelle) - [ ] `archivmail ocr-reprocess` — OCR für alle oder einzelne Mandanten nachholen - [ ] Keine neue externe Service-Abhängigkeit bei Laufzeit — nur System-Pakete (`tesseract-ocr`, `poppler-utils`) ## Edge Cases - Sehr große PDFs (>50 MB): Timeout nach 60s, Status `failed`, Mail bleibt auffindbar per Metadaten - Passwortgeschützte PDFs: OCR überspringen, Status `skipped` - Anhänge ohne OCR-fähiges Format (ZIP, EXE, ...): überspringen - Tesseract nicht installiert: OCR deaktiviert, Warnung beim Start, kein Absturz ## Tech Design ### Neue Komponenten **`internal/ocr/ocr.go`** ```go // ExtractText extrahiert Text aus einem Anhang. // Für PDFs: pdftotext zuerst, dann Tesseract als Fallback. // Für Bilder: direkt Tesseract. // Gibt leeren String zurück wenn Format nicht unterstützt oder Fehler. func ExtractText(data []byte, contentType string, langs []string) (string, error) // IsAvailable prüft ob Tesseract installiert ist. func IsAvailable() bool ``` **`internal/ocr/worker.go`** — Async-Worker ```go // Worker liest aus einem Channel und verarbeitet OCR-Jobs. // Läuft als Goroutine im Hintergrund. type Worker struct { ... } func NewWorker(store *storage.Store, idxMgr index.TenantIndexer) *Worker func (w *Worker) Submit(mailID string, tenantID *int64) func (w *Worker) Start(ctx context.Context) ``` ### DB-Schema ```sql -- OCR-Status pro Mail ALTER TABLE emails ADD COLUMN IF NOT EXISTS ocr_status TEXT DEFAULT 'pending'; -- Werte: pending | done | failed | skipped | disabled -- OCR pro Tenant konfigurierbar ALTER TABLE tenants ADD COLUMN IF NOT EXISTS ocr_enabled BOOLEAN DEFAULT TRUE; -- Index für OCR-Queue CREATE INDEX IF NOT EXISTS idx_emails_ocr_status ON emails (ocr_status) WHERE ocr_status = 'pending'; ``` ### Manticore-Schema-Erweiterung ```sql -- Neues Feld im RT-Index ALTER TABLE emails_tenant_1 ADD COLUMN attachment_text text; ``` In `internal/index/manticore.go`: - `MailDocument.AttachmentText string` hinzufügen - `ensureTable()` — neues Feld im CREATE TABLE - `IndexSync()` — `attachment_text` befüllen ### Verarbeitungs-Ablauf ``` Mail eingehend (SMTP/IMAP/Import) → mailparser.Parse() → Anhänge erkannt → mailStore.Save() → Mail gespeichert, ocr_status = 'pending' → Manticore-Index ohne attachment_text → OCR-Worker.Submit(mailID) OCR-Worker (async, Goroutine): → mailStore.Load(mailID) → Rohdaten → mailparser.Parse() → Anhänge → für jeden Anhang: → ocr.ExtractText(data, contentType, ["deu","eng"]) → idxMgr.ForTenant(tenantID).UpdateAttachmentText(mailID, text) → mailStore.SetOCRStatus(mailID, "done") ``` ### Neues CLI-Subkommando ```bash archivmail ocr-reprocess --config /etc/archivmail/config.yml archivmail ocr-reprocess --config /etc/archivmail/config.yml --tenant 1 archivmail ocr-reprocess --config /etc/archivmail/config.yml --status failed ``` ### Installation auf Server ```bash apt-get install -y tesseract-ocr tesseract-ocr-deu poppler-utils # Prüfen tesseract --version pdftotext -v ``` `update.sh` — optionale Installation (kein Abbruch wenn nicht verfügbar): ```bash apt-get install -y tesseract-ocr tesseract-ocr-deu poppler-utils 2>/dev/null || true ``` ### Performance-Überlegungen | Format | Tool | Dauer (A4-Seite) | |--------|------|-----------------| | PDF mit Text | pdftotext | < 100ms | | PDF als Scan | Tesseract | 1–3s | | JPG (300 DPI) | Tesseract | 0.5–2s | OCR-Worker mit konfigurierbarer Worker-Anzahl (Standard: 2 Goroutinen). Keine Blockierung des Mail-Eingangs — Submit() ist non-blocking. ## Nicht in Scope - Microsoft Word / Excel / PowerPoint direkt (nur wenn als PDF geliefert) - Layout-Analyse oder Tabellen-Extraktion - Sprachen außer Deutsch und Englisch (erweiterbar via Config) - Cloud-OCR-Services (bewusst: nur lokale Tools) ## Tech Design _Vollständig oben beschrieben_ ## QA Test Results _To be added_ ## Deployment _To be added_