subdocument-splitting-plan

This commit is contained in:
rehlert committed 2026-10-06 22:52:35 +02:00
1 parent f09384d2e6
commit ca06ca2050
47 files changed
+4097 -136

No files matched your search

+139
View File
@@ -0,0 +1,139 @@
# 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<PdfMetadataInfo>;
export const stripPdfMetadata = async (bytes: Uint8Array): Promise<Uint8Array>;
```
- `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.