lightweight_pdf_core/watermark.rs
1//! `Watermark` (Phase 6, `plan/phases/phase-6-business-polish.md` step 2):
2//! a rotated diagonal stamp ("ENTWURF", "STORNIERT"). Deliberately **not**
3//! a normal flow `Element` — it's a document-level, independent layer,
4//! always drawn first (bottom) and clipped to the body content box, per
5//! `05-overflow-and-robustness.md`'s explicit requirement that it must
6//! never make normal content unreadable or bleed into the header/footer
7//! bands. No general rotation/transform API is introduced for other
8//! elements (ADR/plan: "kein allgemeine Transform-API").
9
10use crate::style::{Color, FontKey};
11
12#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(deny_unknown_fields))]
13#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
14#[derive(Clone, Debug)]
15pub struct Watermark {
16 pub text: String,
17 /// Counter-clockwise rotation in degrees, applied around the body
18 /// box's center. 45° (bottom-left to top-right diagonal) matches the
19 /// conventional "ENTWURF"/"DRAFT" stamp look.
20 pub rotation_deg: f32,
21 pub size: f32,
22 pub color: Color,
23 pub font: FontKey,
24}
25
26impl Watermark {
27 pub fn new(text: impl Into<String>) -> Self {
28 Watermark {
29 text: text.into(),
30 rotation_deg: 45.0,
31 size: 72.0,
32 // Light gray: legible-by-construction since normal content
33 // always draws *after* (on top of) the watermark, but a light
34 // color keeps the page from looking visually "shouted at".
35 color: Color::rgb(210, 210, 210),
36 font: FontKey::SANS_BOLD,
37 }
38 }
39
40 pub fn rotation(mut self, degrees: f32) -> Self {
41 self.rotation_deg = degrees;
42 self
43 }
44
45 pub fn size(mut self, size: f32) -> Self {
46 self.size = size;
47 self
48 }
49
50 pub fn color(mut self, color: Color) -> Self {
51 self.color = color;
52 self
53 }
54}