Skip to main content

easyofd_core/page_obj/
ct_layer.rs

1//! CT_Layer 图层。
2
3use super::CT_PageBlock;
4
5/// 对应 Java: org.ofdrw.core.basicStructure.pageObj.layer.CT_Layer
6///
7/// 图层类型,用于描述页面中的不同层(正文层、前景层、背景层)。
8/// 继承自 CT_PageBlock,增加了图层类型和绘制参数引用。
9/// 对应 GB/T 33190-2016 第 7.7 节。
10#[allow(non_camel_case_types)]
11#[derive(Debug, Clone)]
12pub struct CT_Layer {
13    /// 图层类型。
14    pub layer_type: LayerType,
15    /// 绘制参数引用 ID(可选)。
16    pub draw_param: Option<u32>,
17    /// 页面块内容。
18    pub block: CT_PageBlock,
19}
20
21/// 图层类型枚举。
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum LayerType {
24    /// 正文层(默认)。
25    Body,
26    /// 前景层。
27    Foreground,
28    /// 背景层。
29    Background,
30}
31
32impl LayerType {
33    /// 转为 OFD XML 属性值。
34    #[must_use]
35    pub fn as_str(&self) -> &'static str {
36        match self {
37            Self::Body => "Body",
38            Self::Foreground => "Foreground",
39            Self::Background => "Background",
40        }
41    }
42}
43
44impl std::fmt::Display for LayerType {
45    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46        f.write_str(self.as_str())
47    }
48}
49
50impl CT_Layer {
51    /// 使用指定类型创建图层。
52    #[must_use]
53    pub fn new(layer_type: LayerType) -> Self {
54        Self {
55            layer_type,
56            draw_param: None,
57            block: CT_PageBlock::new(),
58        }
59    }
60
61    /// 创建正文层。
62    #[must_use]
63    pub fn body() -> Self {
64        Self::new(LayerType::Body)
65    }
66
67    /// 创建前景层。
68    #[must_use]
69    pub fn foreground() -> Self {
70        Self::new(LayerType::Foreground)
71    }
72
73    /// 创建背景层。
74    #[must_use]
75    pub fn background() -> Self {
76        Self::new(LayerType::Background)
77    }
78
79    /// 设置绘制参数引用。
80    #[must_use]
81    pub fn draw_param(mut self, id: u32) -> Self {
82        self.draw_param = Some(id);
83        self
84    }
85
86    /// 设置图层类型。
87    #[must_use]
88    pub fn layer_type(mut self, layer_type: LayerType) -> Self {
89        self.layer_type = layer_type;
90        self
91    }
92
93    /// 获取图层类型。
94    #[must_use]
95    pub fn get_type(&self) -> LayerType {
96        self.layer_type
97    }
98
99    /// 获取绘制参数引用。
100    #[must_use]
101    pub fn get_draw_param(&self) -> Option<u32> {
102        self.draw_param
103    }
104
105    /// 添加嵌套页面块。
106    pub fn add_page_block(&mut self, page_block: CT_PageBlock) {
107        self.block.add_page_block(page_block);
108    }
109
110    /// 序列化为 OFD XML 字符串。
111    #[must_use]
112    pub fn to_xml_string(&self) -> String {
113        use std::fmt::Write;
114        let mut xml = format!("<ofd:Layer Type=\"{}\"", self.layer_type.as_str());
115        if let Some(dp) = self.draw_param {
116            write!(xml, " DrawParam=\"{dp}\"").expect("写入内存缓冲区不会失败");
117        }
118        xml.push_str(">\n");
119        // Inline the block content (skip outer PageBlock tags for layer).
120        for text_obj in &self.block.text_objects {
121            let _ = writeln!(
122                xml,
123                "  <ofd:TextObject ID=\"{}\" Boundary=\"{}\">{}</ofd:TextObject>",
124                text_obj.id, text_obj.boundary, text_obj.content
125            );
126        }
127        for path_obj in &self.block.path_objects {
128            let _ = writeln!(
129                xml,
130                "  <ofd:PathObject ID=\"{}\" Boundary=\"{}\">\
131                 <ofd:AbbreviatedData>{}</ofd:AbbreviatedData>\
132                 </ofd:PathObject>",
133                path_obj.id, path_obj.boundary, path_obj.abbreviated_data
134            );
135        }
136        for img_obj in &self.block.image_objects {
137            let _ = writeln!(
138                xml,
139                "  <ofd:ImageObject ID=\"{}\" Boundary=\"{}\" ResourceID=\"{}\" />",
140                img_obj.id, img_obj.boundary, img_obj.resource_id
141            );
142        }
143        for nested in &self.block.page_blocks {
144            xml.push_str(&nested.to_xml_string());
145        }
146        xml.push_str("</ofd:Layer>\n");
147        xml
148    }
149}
150
151#[cfg(test)]
152mod tests {
153    use super::super::ct_page_block::{PageBlockImageObject, PageBlockTextObject};
154    use super::*;
155
156    #[test]
157    fn test_ct_layer_body() {
158        let layer = CT_Layer::body();
159        assert_eq!(layer.layer_type, LayerType::Body);
160        assert!(layer.draw_param.is_none());
161    }
162
163    #[test]
164    fn test_ct_layer_foreground() {
165        let layer = CT_Layer::foreground();
166        assert_eq!(layer.layer_type, LayerType::Foreground);
167    }
168
169    #[test]
170    fn test_ct_layer_background() {
171        let layer = CT_Layer::background();
172        assert_eq!(layer.layer_type, LayerType::Background);
173    }
174
175    #[test]
176    fn test_ct_layer_builder() {
177        let layer = CT_Layer::new(LayerType::Body).draw_param(42);
178        assert_eq!(layer.get_draw_param(), Some(42));
179    }
180
181    #[test]
182    fn test_layer_type_display() {
183        assert_eq!(LayerType::Body.to_string(), "Body");
184        assert_eq!(LayerType::Foreground.to_string(), "Foreground");
185        assert_eq!(LayerType::Background.to_string(), "Background");
186    }
187
188    #[test]
189    fn test_layer_type_as_str() {
190        assert_eq!(LayerType::Body.as_str(), "Body");
191        assert_eq!(LayerType::Foreground.as_str(), "Foreground");
192        assert_eq!(LayerType::Background.as_str(), "Background");
193    }
194
195    #[test]
196    fn test_ct_layer_to_xml_basic() {
197        let layer = CT_Layer::body();
198        let xml = layer.to_xml_string();
199        assert!(xml.contains("<ofd:Layer"));
200        assert!(xml.contains("Type=\"Body\""));
201        assert!(xml.contains("</ofd:Layer>"));
202    }
203
204    #[test]
205    fn test_ct_layer_to_xml_with_draw_param() {
206        let layer = CT_Layer::foreground().draw_param(7);
207        let xml = layer.to_xml_string();
208        assert!(xml.contains("DrawParam=\"7\""));
209        assert!(xml.contains("Type=\"Foreground\""));
210    }
211
212    #[test]
213    fn test_ct_layer_to_xml_with_content() {
214        let mut layer = CT_Layer::body();
215        layer
216            .block
217            .add_text_object(PageBlockTextObject::new(1, "0 0 50 20", "hi"));
218        layer
219            .block
220            .add_image_object(PageBlockImageObject::new(2, "0 0 100 100", 3));
221        let xml = layer.to_xml_string();
222        assert!(xml.contains("ofd:TextObject"));
223        assert!(xml.contains("hi"));
224        assert!(xml.contains("ofd:ImageObject"));
225    }
226
227    #[test]
228    fn test_ct_layer_clone_debug() {
229        let layer = CT_Layer::body();
230        let layer2 = layer.clone();
231        assert_eq!(layer2.layer_type, LayerType::Body);
232        assert!(format!("{layer:?}").contains("CT_Layer"));
233    }
234}