1use quick_xml::{Reader, Writer, events::Event};
3use std::io::{Cursor, Write};
4use crate::errors::DocxError;
5
6pub(crate) fn validate_docx_format(file_data: &[u8]) -> Result<(), DocxError> {
9 if file_data.len() < 22 {
11 return Err(DocxError::InvalidZipFormat);
12 }
13
14 let signature = u32::from_le_bytes([
18 file_data[0], file_data[1], file_data[2], file_data[3]
19 ]);
20
21 match signature {
22 0x04034b50 | 0x06054b50 | 0x08074b50 => {
23 },
25 _ => return Err(DocxError::InvalidZipFormat),
26 }
27
28 Ok(())
29}
30
31pub(crate) fn merge_handlebars_in_xml(xml_content: String) -> Result<String, Box<dyn std::error::Error>> {
49 if !xml_content.contains('{') {
51 return Ok(xml_content);
52 }
53
54 let mut reader = Reader::from_str(&xml_content);
56 let mut writer = Writer::new(Cursor::new(Vec::new()));
57 let mut buf = Vec::new();
58
59 let mut text_buffer = String::new();
61
62 let mut brace_count = 0; let mut in_handlebars = false; loop {
67 match reader.read_event_into(&mut buf) {
68 Ok(Event::Text(ref e)) => {
70 let text = std::str::from_utf8(e)?;
71
72 for ch in text.chars() {
74 if ch == '{' {
75 brace_count += 1;
76 if brace_count >= 2 {
78 in_handlebars = true;
79 }
80 } else if ch == '}' {
81 if brace_count > 0 {
82 brace_count -= 1;
83 }
84 if brace_count == 0 {
86 in_handlebars = false;
87 }
88 }
89 }
90
91 text_buffer.push_str(text);
93
94 if !in_handlebars && brace_count == 0
96 && !text_buffer.is_empty() {
97 writer.write_event(Event::Text(quick_xml::events::BytesText::from_escaped(&text_buffer)))?;
99 text_buffer.clear();
100 }
101 }
103
104 Ok(Event::Start(ref e)) => {
106 if !in_handlebars && brace_count == 0 {
108 if !text_buffer.is_empty() {
110 writer.write_event(Event::Text(quick_xml::events::BytesText::from_escaped(&text_buffer)))?;
112 text_buffer.clear();
113 }
114 writer.write_event(Event::Start(e.clone()))?;
116 }
117 }
119
120 Ok(Event::End(ref e)) => {
122 if !in_handlebars && brace_count == 0 {
124 if !text_buffer.is_empty() {
126 writer.write_event(Event::Text(quick_xml::events::BytesText::from_escaped(&text_buffer)))?;
128 text_buffer.clear();
129 }
130 writer.write_event(Event::End(e.clone()))?;
132 }
133 }
134
135 Ok(Event::Empty(ref e)) => {
137 if !in_handlebars && brace_count == 0 {
138 if !text_buffer.is_empty() {
139 writer.write_event(Event::Text(quick_xml::events::BytesText::from_escaped(&text_buffer)))?;
141 text_buffer.clear();
142 }
143 writer.write_event(Event::Empty(e.clone()))?;
144 }
145 }
146
147 Ok(Event::Eof) => break,
149 Ok(event) => {
150 if !in_handlebars && brace_count == 0 {
151 if !text_buffer.is_empty() {
152 writer.write_event(Event::Text(quick_xml::events::BytesText::from_escaped(&text_buffer)))?;
154 text_buffer.clear();
155 }
156 writer.write_event(event)?;
157 }
158 }
159
160 Err(e) => return Err(format!("XML解析错误 at position {}: {:?}", reader.buffer_position(), e).into()),
162 }
163 buf.clear();
164 }
165
166 if !text_buffer.is_empty() {
168 writer.write_event(Event::Text(quick_xml::events::BytesText::from_escaped(&text_buffer)))?;
170 }
171
172 let result = writer.into_inner().into_inner();
174 Ok(String::from_utf8(result)?)
175}
176
177pub(crate) fn register_basic_helpers(handlebars: &mut handlebars::Handlebars) -> Result<(), Box<dyn std::error::Error>> {
179 use handlebars::handlebars_helper;
180 use serde_json::Value;
181
182 handlebars_helper!(eq: |x: Value, y: Value| x == y);
184 handlebars.register_helper("eq", Box::new(eq));
185
186 handlebars_helper!(ne: |x: Value, y: Value| x != y);
188 handlebars.register_helper("ne", Box::new(ne));
189
190 handlebars_helper!(gt: |x: i64, y: i64| x > y);
192 handlebars.register_helper("gt", Box::new(gt));
193
194 handlebars_helper!(lt: |x: i64, y: i64| x < y);
196 handlebars.register_helper("lt", Box::new(lt));
197
198 handlebars_helper!(upper: |s: String| s.to_uppercase());
200 handlebars.register_helper("upper", Box::new(upper));
201
202 handlebars_helper!(lower: |s: String| s.to_lowercase());
204 handlebars.register_helper("lower", Box::new(lower));
205
206 handlebars_helper!(len: |x: Value| {
208 match x {
209 Value::Array(arr) => arr.len(),
210 Value::String(s) => s.chars().count(),
211 Value::Object(obj) => obj.len(),
212 _ => 0
213 }
214 });
215 handlebars.register_helper("len", Box::new(len));
216
217 Ok(())
229}
230
231pub(crate) fn remove_table_row_simple(xml_content: &str, target_uuid: &str) -> Result<String, Box<dyn std::error::Error>> {
233 let mut reader = Reader::from_str(xml_content);
234
235 let mut writer = Writer::new(Cursor::new(Vec::new()));
236 let mut buf = Vec::new();
237
238 let mut current_row_content = String::new();
239 let mut in_table_row = false;
240 let mut row_depth = 0;
241
242 loop {
243 match reader.read_event_into(&mut buf) {
244 Ok(Event::Start(ref e)) => {
245 if e.name().as_ref() == b"w:tr" {
246 in_table_row = true;
247 row_depth += 1;
248 current_row_content.clear();
249 current_row_content.push_str(&format!("<{}", String::from_utf8_lossy(e.name().as_ref())));
250 for attr in e.attributes().flatten() {
252 current_row_content.push_str(&format!(" {}=\"{}\"",
253 String::from_utf8_lossy(attr.key.as_ref()),
254 String::from_utf8_lossy(&attr.value)));
255 }
256 current_row_content.push('>');
257 } else if in_table_row {
258 current_row_content.push_str(&format!("<{}", String::from_utf8_lossy(e.name().as_ref())));
259 for attr in e.attributes().flatten() {
261 current_row_content.push_str(&format!(" {}=\"{}\"",
262 String::from_utf8_lossy(attr.key.as_ref()),
263 String::from_utf8_lossy(&attr.value)));
264 }
265 current_row_content.push('>');
266 } else {
267 writer.write_event(Event::Start(e.clone()))?;
268 }
269 }
270 Ok(Event::End(ref e)) => {
271 if e.name().as_ref() == b"w:tr" && in_table_row {
272 row_depth -= 1;
273 if row_depth == 0 {
274 current_row_content.push_str(&format!("</{}>", String::from_utf8_lossy(e.name().as_ref())));
275
276 if !current_row_content.contains(target_uuid) {
278 writer.get_mut().write_all(current_row_content.as_bytes())?;
280 }
281 in_table_row = false;
284 current_row_content.clear();
285 } else {
286 current_row_content.push_str(&format!("</{}>", String::from_utf8_lossy(e.name().as_ref())));
287 }
288 } else if in_table_row {
289 current_row_content.push_str(&format!("</{}>", String::from_utf8_lossy(e.name().as_ref())));
290 } else {
291 writer.write_event(Event::End(e.clone()))?;
292 }
293 }
294 Ok(Event::Text(ref e)) => {
295 if in_table_row {
296 let text = std::str::from_utf8(e)?;
297 current_row_content.push_str(text);
300 } else {
301 writer.write_event(Event::Text(e.clone()))?;
302 }
303 }
304 Ok(Event::Empty(ref e)) => {
305 if in_table_row {
306 current_row_content.push_str(&format!("<{}", String::from_utf8_lossy(e.name().as_ref())));
307 for attr in e.attributes().flatten() {
308 current_row_content.push_str(&format!(" {}=\"{}\"",
309 String::from_utf8_lossy(attr.key.as_ref()),
310 String::from_utf8_lossy(&attr.value)));
311 }
312 current_row_content.push_str("/>");
313 } else {
314 writer.write_event(Event::Empty(e.clone()))?;
315 }
316 }
317 Ok(Event::Comment(ref e)) => {
318 if !in_table_row {
319 writer.write_event(Event::Comment(e.clone()))?;
320 }
321 }
322 Ok(Event::CData(ref e)) => {
323 if in_table_row {
324 current_row_content.push_str(&format!("<![CDATA[{}]]>", String::from_utf8_lossy(e)));
325 } else {
326 writer.write_event(Event::CData(e.clone()))?;
327 }
328 }
329 Ok(Event::Decl(ref e)) => {
330 writer.write_event(Event::Decl(e.clone()))?;
331 }
332 Ok(Event::PI(ref e)) => {
333 if !in_table_row {
334 writer.write_event(Event::PI(e.clone()))?;
335 }
336 }
337 Ok(Event::DocType(ref e)) => {
338 writer.write_event(Event::DocType(e.clone()))?;
339 }
340 Ok(Event::GeneralRef(ref e)) => {
341 if !in_table_row {
342 writer.write_event(Event::GeneralRef(e.clone()))?;
343 }
344 }
345 Ok(Event::Eof) => break,
346 Err(e) => return Err(format!("Error at position {}: {:?}", reader.buffer_position(), e).into()),
347 }
348 buf.clear();
349 }
350
351 let result = writer.into_inner().into_inner();
352 Ok(String::from_utf8(result)?)
353}