Skip to main content

easyofd_core/annotation/
annotations.rs

1//! 注释根容器。
2
3use std::fmt::Write;
4
5use super::ann_page::AnnPage;
6
7/// 对应 Java: org.ofdrw.core.annotation.Annotations
8///
9/// 注释根容器,对应 Annotations.xml,包含文档中所有页的注释索引。
10#[derive(Debug, Clone)]
11pub struct Annotations {
12    /// 注释所属的文档 ID。
13    pub doc_id: Option<String>,
14    /// 各页的注释索引。
15    pub pages: Vec<AnnPage>,
16}
17
18impl Annotations {
19    /// 创建一个空的注释容器。
20    #[must_use]
21    pub fn new() -> Self {
22        Self {
23            doc_id: None,
24            pages: Vec::new(),
25        }
26    }
27
28    /// 设置文档 ID。
29    #[must_use]
30    pub fn doc_id(mut self, id: impl Into<String>) -> Self {
31        self.doc_id = Some(id.into());
32        self
33    }
34
35    /// 添加注释页。
36    #[must_use]
37    pub fn add_page(mut self, page: AnnPage) -> Self {
38        self.pages.push(page);
39        self
40    }
41
42    /// 序列化为 XML 字符串。
43    #[must_use]
44    pub fn to_xml_string(&self) -> String {
45        let mut xml = String::from(r#"<?xml version="1.0" encoding="UTF-8"?>"#);
46        xml.push('\n');
47        xml.push_str(r#"<ofd:Annotations xmlns:ofd="http://www.ofdspec.org/2016""#);
48        if let Some(ref doc_id) = self.doc_id {
49            let _ = write!(xml, r#" DocID="{doc_id}""#);
50        }
51        xml.push('>');
52        for page in &self.pages {
53            xml.push('\n');
54            xml.push_str(&page.to_xml_string());
55        }
56        xml.push_str("\n</ofd:Annotations>");
57        xml
58    }
59}
60
61impl Default for Annotations {
62    fn default() -> Self {
63        Self::new()
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    #[test]
72    fn test_annotations_new() {
73        let a = Annotations::new();
74        assert!(a.doc_id.is_none());
75        assert!(a.pages.is_empty());
76    }
77
78    #[test]
79    fn test_annotations_default() {
80        let a = Annotations::default();
81        assert!(a.pages.is_empty());
82    }
83
84    #[test]
85    fn test_annotations_builder() {
86        let a = Annotations::new()
87            .doc_id("doc-001")
88            .add_page(AnnPage::new(0))
89            .add_page(AnnPage::new(1));
90        assert_eq!(a.doc_id.as_deref(), Some("doc-001"));
91        assert_eq!(a.pages.len(), 2);
92    }
93
94    #[test]
95    fn test_annotations_to_xml_string() {
96        let a = Annotations::new()
97            .doc_id("doc-abc")
98            .add_page(AnnPage::new(0));
99        let xml = a.to_xml_string();
100        assert!(xml.contains(r#"<?xml version="1.0""#));
101        assert!(xml.contains("ofd:Annotations"));
102        assert!(xml.contains(r#"DocID="doc-abc""#));
103        assert!(xml.contains(r#"PageIndex="0""#));
104        assert!(xml.contains("</ofd:Annotations>"));
105    }
106
107    #[test]
108    fn test_annotations_to_xml_string_no_doc_id() {
109        let a = Annotations::new();
110        let xml = a.to_xml_string();
111        assert!(!xml.contains("DocID"));
112        assert!(xml.contains("</ofd:Annotations>"));
113    }
114
115    #[test]
116    fn test_annotations_clone_debug() {
117        let a = Annotations::new().doc_id("x");
118        let a2 = a.clone();
119        assert_eq!(a2.doc_id.as_deref(), Some("x"));
120        assert!(format!("{a:?}").contains("Annotations"));
121    }
122}