140 lines
6.2 KiB
Markdown
140 lines
6.2 KiB
Markdown
# Plan: Stempeln — Textstempel auf PDFs
|
||
|
||
## Goal
|
||
|
||
Add a new tool **"Stempeln"** (`/tools/stamp`) that places opaque, framed text stamps on PDF
|
||
pages — "BEGLAUBIGT", "EILT", "ENTWURF", "KOPIE", "AUSFERTIGUNG", plus free text with an optional
|
||
date, or an image. This is a staple of German legal/case-file work and is deliberately separate from the
|
||
watermark tool:
|
||
|
||
- Watermark = semi-transparent, decorative/repetitive, many positions.
|
||
- Stamp = fully opaque, framed, authoritative marking, usually one per page.
|
||
|
||
Pure pdf-lib drawing (text + rectangle border): lossless, instant. No new dependencies.
|
||
|
||
---
|
||
|
||
## Open decisions (defaults marked ✅)
|
||
|
||
### D1 — Stamp look
|
||
|
||
- ✅ **A. Uppercase bold text inside a plain rectangle border** (2 pt border, padding 8 pt,
|
||
default color red `#cc1a1a`, matching the existing brand red used by the watermark default).
|
||
pdf-lib has no rounded-rectangle primitive; plain rectangle is the honest option.
|
||
- B. Borderless colored text. Possible but reads as a watermark, not a stamp.
|
||
|
||
### D2 — Presets
|
||
|
||
- ✅ **Editable preset chips + free text.** Chips: BEGLAUBIGT, AUSFERTIGUNG, KOPIE, EILT,
|
||
ENTWURF, ERLEDIGT. Selecting a chip fills the text input; the input stays editable. Optional
|
||
checkbox "Datum anhängen" appends `dd.mm.yyyy` (German format).
|
||
- B. Fixed presets only. Rejected: courts/firms love their own wording.
|
||
|
||
### D3 — Relation to the watermark tool
|
||
|
||
- ✅ **Separate tool**, sharing extracted helpers from `src/lib/pdf-overlay.ts`
|
||
(position coordinate computation + hex color parsing — see page-numbers-plan; whichever plan is
|
||
implemented first creates the module). Keeps discovery simple (one tile per job) and avoids
|
||
overloading the watermark UI with a mode switch.
|
||
|
||
---
|
||
|
||
## UX
|
||
|
||
- Route `/tools/stamp`, new `src/lib/tools.ts` entry:
|
||
`{ slug: 'stamp', title: 'Stempeln', description: 'Versehen Sie Seiten mit Textstempeln wie „Beglaubigt“ oder „Eilt“.', icon: 'stamp' }`
|
||
(`stamp` is a real Lucide icon name) + icon map entry.
|
||
- After load (`loadPdfDocument` for thumbnails):
|
||
- **Options card**:
|
||
- Preset chip row (buttons; selected chip highlighted).
|
||
- Text input (bound, default "BEGLAUBIGT"), "Datum anhängen" checkbox.
|
||
- Schriftgröße slider (12–48 pt, default 24).
|
||
- Farbe color input (default `#cc1a1a`).
|
||
- Position select — same six positions + center as watermark
|
||
(reuses shared position helper; no "diagonal" for stamps).
|
||
- Rotation slider (−45°…45°, default 0) — stamps are often applied slightly tilted.
|
||
- Apply-to: "Auf alle Seiten anwenden" checkbox + `PdfPageGrid` selection when off
|
||
(watermark pattern).
|
||
- **Live preview**: rendered current page (pdfjs) with an absolutely-positioned overlay div
|
||
styled like the stamp (border, uppercase, color, rotation) at the chosen position; page
|
||
navigation. Approximation is fine, same approach as page-numbers-plan.
|
||
- Primary button **"Stempel anwenden & herunterladen"** → `{baseName}_stempel.pdf`.
|
||
|
||
## Technical approach
|
||
|
||
### Export (pdf-lib)
|
||
|
||
```ts
|
||
const font = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
|
||
const stampText = `${textInput.trim().toUpperCase()}${withDate ? ` ${formatGermanDate(new Date())}` : ''}`;
|
||
const textWidth = font.widthOfTextAtSize(stampText, size);
|
||
const boxWidth = textWidth + 2 * padding;
|
||
const boxHeight = font.heightAtSize(size) + 2 * padding;
|
||
|
||
for (const pageIndex of targetPages) {
|
||
const { width, height } = pages[pageIndex].getSize();
|
||
const { x, y } = getPositionCoords(width, height, boxWidth, boxHeight, position); // shared helper
|
||
pages[pageIndex].drawRectangle({
|
||
x, y, width: boxWidth, height: boxHeight,
|
||
borderColor: parseHexColor(colorValue), borderWidth: 2
|
||
});
|
||
pages[pageIndex].drawText(stampText, {
|
||
x: x + padding, y: y + padding + /* baseline adjust */, size, font,
|
||
color: parseHexColor(colorValue), rotate: degrees(rotationValue)
|
||
});
|
||
}
|
||
```
|
||
|
||
Details:
|
||
|
||
- Rotation is applied to the text; the border rectangle stays axis-aligned for v1 (rotated
|
||
rectangles need manual path drawing — follow-up if users care).
|
||
- Baseline adjustment: use `font.heightAtSize(size) * 0.2` descent approximation; verify visually.
|
||
- Uppercasing happens in JS (`toUpperCase()` — German umlauts are fine in WinAnsi: ä/ö/ü/ß are
|
||
supported except ẞ; uppercase ß → SS via `toLocaleUpperCase('de')` acceptable).
|
||
- Encrypted input → German error + decrypt hint (shared pattern).
|
||
|
||
### Shared helpers
|
||
|
||
Depends on `src/lib/pdf-overlay.ts` (position presets + `parseHexColor`). If page-numbers or
|
||
stamp ships first, it creates the module and rewires watermark; the second consumer just imports.
|
||
|
||
## Files changed
|
||
|
||
- `src/routes/tools/stamp/+page.svelte` — new route.
|
||
- `src/lib/pdf-overlay.ts` — shared helpers (created here if not already present).
|
||
- `src/lib/tools.ts`, `src/routes/+page.svelte` — registry + icon map.
|
||
- `tests/e2e/toolbox.spec.ts` — spec block.
|
||
- `agents.md` — structure update.
|
||
|
||
## Implementation phases
|
||
|
||
1. Shared overlay helpers (or import if they exist) — watermark regression must stay green.
|
||
2. Route UI: presets, inputs, preview overlay, page selection.
|
||
3. Export + download.
|
||
4. Registry + icon.
|
||
5. Playwright: preset "EILT" on all pages of a 3-page PDF → output valid, 3 pages; custom text
|
||
- date checkbox adds text; selection-only mode leaves other pages unmodified (compare page
|
||
content-stream sizes via pdf-lib if practical, else file-level validity + count).
|
||
6. Polish + command gauntlet.
|
||
|
||
## Verification
|
||
|
||
- Every preset chip fills the input; the stamp renders uppercase in preview and output.
|
||
- Date uses German `dd.mm.yyyy` and updates on the export day (not cached).
|
||
- Rotation visually matches preview within tolerance.
|
||
- Watermark tool unchanged (regression).
|
||
|
||
## Risks
|
||
|
||
- **Text width overflow** on small pages with long custom text + huge font: clamp computed box to
|
||
page size minus margins and shrink font if needed (simple guard loop), with a German hint.
|
||
- Preview overlay approximation may differ a few points from export — acceptable, labeled.
|
||
|
||
## Out of scope
|
||
|
||
- Image stamps (logo stamps) — users can route through the watermark image mode for that.
|
||
- Rotated border rectangles, multi-stamps per page in one run (run the tool twice; output can be
|
||
re-loaded).
|
||
- Stamp "libraries" persisted across sessions.
|