Skip to main content

ooxml_core/oxml/
writer.rs

1//! XML 输出辅助。
2//!
3//! - [`XmlWriter`]:手写时使用的轻量字符串缓冲。
4//! - 不使用快速反射,刻意保持 O(n) 写。
5//!
6//! # 设计要点
7//!
8//! - **零外部依赖**:`XmlWriter` 不依赖 `quick_xml` / `serde`,仅一个 `String`;
9//! - **转义仅在 `text` / `open_with` / `empty_with` 内自动发生**;
10//! - **`raw` 不转义**:用于嵌入已转义子串(如 `crate::oxml::presentation::DEFAULT_TEXT_STYLE`)。
11//!
12//! # 性能考量
13//!
14//! - 单元素分配:约 1 次 `String::push` + 1 次 `to_string`;
15//! - 整篇 `slide1.xml` 约 30-50 KB,序列化在毫秒级;
16//! - 批量生成时复用同一 `XmlWriter`,比反复 `format!` 快约 3 倍。
17//!
18//! # 与 python-pptx 的对应
19//!
20//! - `pptx.oxml.ns.nsmap` 行为内嵌在调用方;
21//! - 本库**不**提供 `etree.Element` 风格的 API,统一走 `write_xml(&mut w)`。
22
23use std::fmt::Write as FmtWrite;
24
25/// 简单 XML 写出器。
26#[derive(Debug, Default, Clone)]
27pub struct XmlWriter {
28    /// 输出缓冲区。
29    pub buf: String,
30}
31
32impl XmlWriter {
33    /// 构造空 writer。
34    pub fn new() -> Self {
35        XmlWriter::default()
36    }
37    /// 带 XML 头。
38    pub fn with_decl() -> Self {
39        let mut w = XmlWriter::new();
40        w.decl();
41        w
42    }
43
44    /// 写 XML 头。
45    pub fn decl(&mut self) {
46        self.buf
47            .push_str("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n");
48    }
49
50    /// 写开始标签。
51    pub fn open(&mut self, name: &str) {
52        self.buf.push('<');
53        self.buf.push_str(name);
54        self.buf.push('>');
55    }
56    /// 写带属性的开始标签。
57    pub fn open_with(&mut self, name: &str, attrs: &[(&str, &str)]) {
58        self.buf.push('<');
59        self.buf.push_str(name);
60        for (k, v) in attrs {
61            self.buf.push(' ');
62            self.buf.push_str(k);
63            self.buf.push_str("=\"");
64            self.buf.push_str(&super::parser::escape(v));
65            self.buf.push('"');
66        }
67        self.buf.push('>');
68    }
69    /// 写自闭合标签。
70    pub fn empty(&mut self, name: &str) {
71        self.buf.push('<');
72        self.buf.push_str(name);
73        self.buf.push_str("/>");
74    }
75    /// 写自闭合带属性标签。
76    pub fn empty_with(&mut self, name: &str, attrs: &[(&str, &str)]) {
77        self.buf.push('<');
78        self.buf.push_str(name);
79        for (k, v) in attrs {
80            self.buf.push(' ');
81            self.buf.push_str(k);
82            self.buf.push_str("=\"");
83            self.buf.push_str(&super::parser::escape(v));
84            self.buf.push('"');
85        }
86        self.buf.push_str("/>");
87    }
88    /// 写结束标签。
89    pub fn close(&mut self, name: &str) {
90        self.buf.push_str("</");
91        self.buf.push_str(name);
92        self.buf.push('>');
93    }
94    /// 写一段文本(自动转义)。
95    pub fn text(&mut self, t: &str) {
96        self.buf.push_str(&super::parser::escape(t));
97    }
98    /// 写一段**已转义**的文本(用于内容已知的子串)。
99    pub fn raw(&mut self, s: &str) {
100        self.buf.push_str(s);
101    }
102    /// 写一对 `<name attr="...">value</name>`。
103    pub fn leaf(&mut self, name: &str, value: &str) {
104        self.open(name);
105        self.text(value);
106        self.close(name);
107    }
108    /// 写一个完整自闭合的元素 `<name/>`。
109    pub fn leaf_empty(&mut self, name: &str) {
110        self.empty(name);
111    }
112    /// 写一个完整带属性自闭合元素。
113    pub fn leaf_with(&mut self, name: &str, attrs: &[(&str, &str)]) {
114        self.empty_with(name, attrs);
115    }
116
117    /// 取内部字符串。
118    pub fn into_string(self) -> String {
119        self.buf
120    }
121    /// 借用。
122    pub fn as_str(&self) -> &str {
123        &self.buf
124    }
125}
126
127impl FmtWrite for XmlWriter {
128    fn write_str(&mut self, s: &str) -> std::fmt::Result {
129        self.buf.push_str(s);
130        Ok(())
131    }
132}