subdocument-splitting-plan
This commit is contained in:
1 parent
f09384d2e6
commit
ca06ca2050
47 files changed
+4097
-136
No files matched your search
@@ -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.
|
||||
Reference in new issue
Block a user