# Plan: PDF-Metadaten — anzeigen und bereinigen ## Goal Add a new tool **"Metadaten"** (`/tools/metadata`) with two jobs: 1. **Inspect** a PDF: show document metadata (title, author, subject, keywords, creator, producer, creation/modification dates), page count, file size, and encryption state — lawyers need to know what a document reveals before forwarding it. 2. **Clean** a PDF: strip the metadata fields and re-save, so the cleaned file can be shared without leaking software, author names, or edit history. No rasterization is involved — this tool works directly with pdf-lib and preserves the document fully (vector text stays selectable). No new dependencies. --- ## Open decisions (defaults marked ✅) ### D1 — Cleaning strategy - ✅ **A. Neutralize fields + re-save with pdf-lib.** Set title/author/subject/keywords to empty strings, creator/producer to neutral values, reset creation/modification dates to the cleaning moment. Saving through pdf-lib rebuilds the file without unreferenced objects. Simple and dependency-free. - B. Also attempt to remove embedded files/attachments. pdf-lib has no reliable public API to enumerate or remove all embedded-file entries; defer to D2. ### D2 — Embedded-file detection - ✅ **A. Best-effort heuristic warning.** Scan the raw bytes for `/EmbeddedFile` and `/EmbeddedFiles` markers; if found, show "Die Datei enthält möglicherweise eingebettete Anlagen. Diese werden beim Bereinigen nicht entfernt." No removal in v1. - B. No detection at all. Rejected: silent ignorance is worse than an honest warning. ### D3 — Encrypted PDFs - ✅ **A. Friendly rejection.** pdf-lib cannot load encrypted files; show a German hint pointing to `tools/decrypt` first. No silent failure. --- ## UX - Route `/tools/metadata`, new entry in `src/lib/tools.ts`: `{ slug: 'metadata', title: 'Metadaten', description: 'Zeigen Sie Metadaten eines PDFs an und entfernen Sie sie.', icon: 'info' }` - icon mapping on the start page. - Flow: `PdfDropzone` (single PDF) → results card with: - **Info table** (label/value rows, empty values shown as "–"): Titel, Autor, Betreff, Schlüsselwörter, Erstellt mit (creator), PDF-Erzeuger (producer), Erstellt am, Geändert am, Seiten, Größe, Verschlüsselung (Ja/Nein — from load success/failure is insufficient; use pdf-lib's load error for encrypted detection and show it as a state, or report "Unbekannt" when not determinable). - **Embedded-file warning block** when the heuristic fires (amber styling consistent with existing warning patterns). - **Quality note**: "Beim Bereinigen bleibt der Inhalt vollständig erhalten (kein Qualitätsverlust)." - Actions: - **"Bereinigt herunterladen"** (primary, disabled for encrypted files) → cleaned PDF named `{baseName}_bereinigt.pdf`. - Secondary: "Original herunterladen" is _not_ offered (the user already has the original). - German inline error area for corrupt/encrypted files, matching existing tool pages. ## Technical approach New route `src/routes/tools/metadata/+page.svelte` plus a small lib module `src/lib/pdf-metadata.ts`: ```ts export type PdfMetadataInfo = { title: string; author: string; subject: string; keywords: string[]; creator: string; producer: string; creationDate: Date | null; modificationDate: Date | null; pageCount: number; embeddedFileSuspected: boolean; }; export const readPdfMetadata = async (bytes: Uint8Array): Promise; export const stripPdfMetadata = async (bytes: Uint8Array): Promise; ``` - `readPdfMetadata`: `PDFDocument.load(bytes)` (with `{ ignoreEncryption: false }` default so encrypted files throw), then the `getTitle()`/`getAuthor()`/`getSubject()`/`getKeywords()`/ `getCreator()`/`getProducer()`/`getCreationDate()`/`getModificationDate()`/`getPageCount()` accessors. Encryption detection: catch the pdf-lib `EncryptedPDFError` and return a distinct `{ encrypted: true }` result shape (discriminated union) so the UI can branch. - `stripPdfMetadata`: load, apply setters (`setTitle('')`, `setAuthor('')`, `setSubject('')`, `setKeywords([])`, `setCreator('')`, `setProducer('beA-Edit')`, `setCreationDate(new Date())`, `setModificationDate(new Date())`), `save()`. - Embedded-file heuristic: decode bytes with `TextDecoder` once and search for the two marker strings; cheap, runs before pdf-lib load. - Reading metadata does **not** need pdfjs or thumbnails — no rasterization limits apply; large files are fine because pdf-lib parses lazily enough for this purpose. ## Files changed - `src/lib/pdf-metadata.ts` — new. - `src/routes/tools/metadata/+page.svelte` — new route. - `src/lib/tools.ts`, `src/routes/+page.svelte` — registry entry + icon map. - `tests/e2e/toolbox.spec.ts` — new spec block. - `agents.md` — structure update. ## Implementation phases 1. `pdf-metadata.ts` with read + strip + heuristic. 2. Route UI: info table, warning block, clean action, encrypted-file branch. 3. Registry entry + icon. 4. Playwright tests: PDF created with `createPdf` + set metadata fields → fields displayed; clean download loads with pdf-lib and has empty author/title; encrypted-PDF rejection hint (reuse the encrypt tool to produce a fixture inline). 5. Polish, German copy review, `pnpm check`/`lint`/`build`/`test:e2e`. ## Verification - Metadata of a known PDF renders completely; empty fields show "–". - Cleaned PDF: open in viewer, metadata fields empty; content and page count identical; file still selectable/searchable (no rasterization happened). - Encrypted input shows the decrypt hint instead of crashing. - `/EmbeddedFile`-containing fixture triggers the amber warning. - Standard command gauntlet passes. ## Risks - **XMP metadata**: pdf-lib's setters touch the Info dict; a separate XMP metadata stream can survive. Mitigation: note in the UI copy that cleaning covers standard document properties. (Full XMP removal could be a follow-up by deleting the metadata stream object.) - **Producer fingerprint**: setting `beA-Edit` as producer is itself a small fingerprint; neutral but acceptable — alternative is an empty string; pick empty string if reviewers prefer. ## Out of scope - Removing embedded files/attachments (see D2). - XMP stream removal (follow-up). - Metadata editing (rename title, etc.) — inspection + removal only. - Hooking metadata cleaning into the beA export pipeline.