Skip to main content

easyofd_core/
watermark.rs

1//! 水印和注解支持。
2
3/// 水印配置。支持文本水印和图片水印。
4#[derive(Debug, Clone)]
5pub struct Watermark {
6    /// 水印文本(文本水印)。
7    pub text: Option<String>,
8    /// 水印图片数据(图片水印)。
9    pub image: Option<Vec<u8>>,
10    /// 位置 (x, y) mm。
11    pub position: (f64, f64),
12    /// 文本水印字号。
13    pub font_size: f64,
14    /// 文本水印字体。
15    pub font: String,
16    /// 文本颜色 RGB hex。
17    pub color: u32,
18    /// 透明度 0.0(透明)– 1.0(不透明)。默认 0.3。
19    pub opacity: f64,
20    /// 旋转角度(度)。默认 45.0(对角线)。
21    pub rotation: f64,
22    /// 目标页码(1-based)。None = 所有页。
23    pub page: Option<usize>,
24}
25
26impl Default for Watermark {
27    fn default() -> Self {
28        Self {
29            text: None,
30            image: None,
31            position: (0.0, 0.0),
32            font_size: 24.0,
33            font: "SimSun".into(),
34            color: 0xCC_CC_CC,
35            opacity: 0.3,
36            rotation: 45.0,
37            page: None,
38        }
39    }
40}
41
42impl Watermark {
43    /// 创建文本水印。
44    #[must_use]
45    pub fn text(content: impl Into<String>) -> Self {
46        Self {
47            text: Some(content.into()),
48            ..Self::default()
49        }
50    }
51
52    /// 设置位置。
53    #[must_use]
54    pub fn position(mut self, x: f64, y: f64) -> Self {
55        self.position = (x, y);
56        self
57    }
58
59    /// 设置字号。
60    #[must_use]
61    pub fn font_size(mut self, size: f64) -> Self {
62        self.font_size = size;
63        self
64    }
65
66    /// 设置透明度。
67    #[must_use]
68    pub fn opacity(mut self, opacity: f64) -> Self {
69        self.opacity = opacity;
70        self
71    }
72
73    /// 设置旋转角度。
74    #[must_use]
75    pub fn rotation(mut self, degrees: f64) -> Self {
76        self.rotation = degrees;
77        self
78    }
79
80    /// 设置目标页码。
81    #[must_use]
82    pub fn page(mut self, page: usize) -> Self {
83        self.page = Some(page);
84        self
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91
92    #[test]
93    fn test_watermark_default() {
94        let wm = Watermark::default();
95        assert!((wm.font_size - 24.0).abs() < f64::EPSILON);
96        assert!((wm.opacity - 0.3).abs() < f64::EPSILON);
97        assert!((wm.rotation - 45.0).abs() < f64::EPSILON);
98        assert!(wm.text.is_none());
99        assert!(wm.page.is_none());
100    }
101
102    #[test]
103    fn test_watermark_text_builder() {
104        let wm = Watermark::text("CONFIDENTIAL")
105            .position(50.0, 100.0)
106            .font_size(36.0)
107            .opacity(0.5)
108            .rotation(30.0)
109            .page(1);
110        assert_eq!(wm.text.as_deref(), Some("CONFIDENTIAL"));
111        assert_eq!(wm.position, (50.0, 100.0));
112        assert!((wm.font_size - 36.0).abs() < f64::EPSILON);
113        assert!((wm.opacity - 0.5).abs() < f64::EPSILON);
114        assert!((wm.rotation - 30.0).abs() < f64::EPSILON);
115        assert_eq!(wm.page, Some(1));
116    }
117
118    #[test]
119    fn test_watermark_clone_debug() {
120        let wm = Watermark::text("test");
121        let wm2 = wm.clone();
122        assert_eq!(wm2.text, wm.text);
123        assert!(format!("{wm:?}").contains("Watermark"));
124    }
125}