Files
bea-edit/plan/done/stamp-plan.md
2026-10-06 22:52:35 +02:00

6.2 KiB
Raw Permalink Blame History

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)

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.