Skip to main content

docx_rs/reader/
run.rs

1#![allow(clippy::single_match)]
2
3use std::io::Read;
4use std::str::FromStr;
5
6use super::Run;
7
8use crate::escape::replace_escaped;
9use crate::types::BreakType;
10use crate::{reader::*, FieldCharType};
11
12#[derive(PartialEq, Debug)]
13enum TextState {
14    Idle,
15    Text,
16    Delete,
17}
18
19fn read_field_char(attributes: &[OwnedAttribute]) -> Result<FieldChar, ReaderError> {
20    let mut t: Option<FieldCharType> = None;
21    let mut dirty = false;
22    for a in attributes {
23        let local_name = &a.name.local_name;
24        match local_name.as_str() {
25            "fldCharType" => {
26                if let Ok(ty) = FieldCharType::from_str(&a.value) {
27                    t = Some(ty);
28                }
29            }
30            "dirty" => {
31                dirty = !is_false(&a.value);
32            }
33            _ => {}
34        }
35    }
36
37    if let Some(t) = t {
38        let mut f = FieldChar::new(t);
39        if dirty {
40            f = f.dirty();
41        }
42        Ok(f)
43    } else {
44        Err(ReaderError::XMLReadError)
45    }
46}
47
48impl ElementReader for Run {
49    fn read<R: Read>(
50        r: &mut EventReader<R>,
51        _attrs: &[OwnedAttribute],
52    ) -> Result<Self, ReaderError> {
53        let mut run = Run::new();
54        let mut text_state = TextState::Idle;
55        loop {
56            let e = r.next();
57            match e {
58                Ok(XmlEvent::StartElement {
59                    attributes, name, ..
60                }) => {
61                    match name.prefix.as_deref() {
62                        Some("w") => {
63                            let e = XMLElement::from_str(&name.local_name).unwrap();
64
65                            ignore::ignore_element(e.clone(), XMLElement::RunPropertyChange, r);
66
67                            match e {
68                                XMLElement::Tab => {
69                                    run = run.add_tab();
70                                }
71                                XMLElement::PTab => {
72                                    if let Ok(v) = PositionalTab::read(r, &attributes) {
73                                        run = run.add_ptab(v);
74                                    }
75                                }
76                                XMLElement::Sym => {
77                                    if let Some(font) = read(&attributes, "font") {
78                                        if let Some(char) = read(&attributes, "char") {
79                                            let sym = Sym::new(font, char);
80                                            run = run.add_sym(sym);
81                                        }
82                                    }
83                                }
84                                XMLElement::RunProperty => {
85                                    let p = RunProperty::read(r, &attributes)?;
86                                    run = run.set_property(p);
87                                }
88                                XMLElement::Text => text_state = TextState::Text,
89                                XMLElement::DeleteText => text_state = TextState::Delete,
90                                XMLElement::Break => {
91                                    if let Some(a) = attributes.first() {
92                                        run = run.add_break(BreakType::from_str(&a.value)?)
93                                    } else {
94                                        run = run.add_break(BreakType::TextWrapping)
95                                    }
96                                }
97                                XMLElement::Drawing => {
98                                    if let Ok(drawing) = Drawing::read(r, &attributes) {
99                                        run = run.add_drawing(drawing);
100                                    }
101                                }
102                                XMLElement::FieldChar => {
103                                    if let Ok(f) = read_field_char(&attributes) {
104                                        run.children.push(RunChild::FieldChar(f));
105                                    }
106                                }
107                                XMLElement::InstrText => loop {
108                                    let e = r.next();
109                                    match e {
110                                        Ok(XmlEvent::Characters(c)) => {
111                                            run.children.push(RunChild::InstrTextString(c));
112                                            break;
113                                        }
114                                        Ok(XmlEvent::EndElement { name, .. }) => {
115                                            let e = XMLElement::from_str(&name.local_name).unwrap();
116                                            match e {
117                                                XMLElement::Run => {
118                                                    return Ok(run);
119                                                }
120                                                _ => {}
121                                            }
122                                        }
123                                        Err(_) => return Err(ReaderError::XMLReadError),
124                                        _ => {}
125                                    }
126                                },
127                                _ => {}
128                            }
129                        }
130                        Some("mc") => {
131                            let e = McXMLElement::from_str(&name.local_name).unwrap();
132                            match e {
133                                McXMLElement::Fallback => {
134                                    let _ = McFallback::read(r, &attributes)?;
135                                }
136                                _ => {}
137                            }
138                        }
139                        Some("v") => {
140                            let e = VXMLElement::from_str(&name.local_name).unwrap();
141                            match e {
142                                // Experimental For now support only imageData in shape
143                                VXMLElement::Shape => {
144                                    if let Ok(shape) = Shape::read(r, &attributes) {
145                                        run.children.push(RunChild::Shape(Box::new(shape)));
146                                    }
147                                }
148                                _ => {}
149                            }
150                        }
151                        _ => {}
152                    };
153                }
154                Ok(XmlEvent::Characters(c)) => match text_state {
155                    TextState::Delete => {
156                        run = run.add_delete_text_without_escape(replace_escaped(&c));
157                    }
158                    TextState::Text => {
159                        run = run.add_text_without_escape(replace_escaped(&c));
160                    }
161                    _ => {}
162                },
163                Ok(XmlEvent::Whitespace(c)) => match text_state {
164                    TextState::Delete => {
165                        run = run.add_delete_text_without_escape(replace_escaped(&c));
166                    }
167                    TextState::Text => {
168                        run = run.add_text_without_escape(replace_escaped(&c));
169                    }
170                    _ => {}
171                },
172                Ok(XmlEvent::EndElement { name, .. }) => {
173                    let e = XMLElement::from_str(&name.local_name).unwrap();
174                    match e {
175                        XMLElement::Run => {
176                            return Ok(run);
177                        }
178                        XMLElement::DeleteText | XMLElement::Text => text_state = TextState::Idle,
179                        _ => {}
180                    }
181                }
182                Err(_) => return Err(ReaderError::XMLReadError),
183                _ => {}
184            }
185        }
186    }
187}
188
189#[cfg(test)]
190mod tests {
191
192    use super::*;
193    #[cfg(test)]
194    use pretty_assertions::assert_eq;
195
196    #[test]
197    fn test_read_size_color() {
198        let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
199  <w:r><w:rPr><w:color w:val="C9211E"/><w:sz w:val="30"/><w:szCs w:val="30"/></w:rPr><w:t>H</w:t></w:r>
200</w:document>"#;
201        let mut parser = EventReader::new(c.as_bytes());
202        let run = Run::read(&mut parser, &[]).unwrap();
203        assert_eq!(
204            run,
205            Run {
206                children: vec![RunChild::Text(Text::new("H"))],
207                run_property: RunProperty {
208                    sz: Some(Sz::new(30)),
209                    sz_cs: Some(SzCs::new(30)),
210                    color: Some(Color::new("C9211E")),
211                    ..RunProperty::default()
212                },
213            }
214        );
215    }
216
217    #[test]
218    fn test_read_tab() {
219        let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
220  <w:r><w:tab /></w:r>
221</w:document>"#;
222        let mut parser = EventReader::new(c.as_bytes());
223        let run = Run::read(&mut parser, &[]).unwrap();
224        assert_eq!(
225            run,
226            Run {
227                children: vec![RunChild::Tab(Tab::new())],
228                run_property: RunProperty::default(),
229            }
230        );
231    }
232
233    #[test]
234    fn test_read_br() {
235        let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
236  <w:r><w:br w:type="page" /></w:r>
237</w:document>"#;
238        let mut parser = EventReader::new(c.as_bytes());
239        let run = Run::read(&mut parser, &[]).unwrap();
240        assert_eq!(
241            run,
242            Run {
243                children: vec![RunChild::Break(Break::new(BreakType::Page))],
244                run_property: RunProperty::default(),
245            }
246        );
247    }
248
249    #[test]
250    fn test_read_empty_br() {
251        let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
252  <w:r><w:br /></w:r>
253</w:document>"#;
254        let mut parser = EventReader::new(c.as_bytes());
255        let run = Run::read(&mut parser, &[]).unwrap();
256        assert_eq!(
257            run,
258            Run {
259                children: vec![RunChild::Break(Break::new(BreakType::TextWrapping))],
260                run_property: RunProperty::default(),
261            }
262        );
263    }
264
265    #[test]
266    fn test_read_italic_false() {
267        let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
268  <w:r><w:rPr>
269    <w:b w:val="true"/>
270    <w:i w:val="false"/>
271  </w:rPr></w:r>
272</w:document>"#;
273        let mut parser = EventReader::new(c.as_bytes());
274        let run = Run::read(&mut parser, &[]).unwrap();
275        assert_eq!(
276            run,
277            Run {
278                children: vec![],
279                run_property: RunProperty {
280                    bold: Some(Bold::new()),
281                    bold_cs: Some(BoldCs::new()),
282                    italic: Some(Italic::new().disable()),
283                    italic_cs: Some(ItalicCs::new().disable()),
284                    ..RunProperty::default()
285                },
286            }
287        );
288    }
289
290    #[test]
291    fn test_read_italic_0() {
292        let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
293  <w:r><w:rPr>
294    <w:b w:val="1"/>
295    <w:i w:val="0"/>
296  </w:rPr></w:r>
297</w:document>"#;
298        let mut parser = EventReader::new(c.as_bytes());
299        let run = Run::read(&mut parser, &[]).unwrap();
300        assert_eq!(
301            run,
302            Run {
303                children: vec![],
304                run_property: RunProperty {
305                    bold: Some(Bold::new()),
306                    bold_cs: Some(BoldCs::new()),
307                    italic: Some(Italic::new().disable()),
308                    italic_cs: Some(ItalicCs::new().disable()),
309                    ..RunProperty::default()
310                },
311            }
312        );
313    }
314
315    #[test]
316    fn test_read_on_off_values() {
317        let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
318  <w:r><w:rPr>
319    <w:b w:val="off"/>
320    <w:i w:val="on"/>
321  </w:rPr></w:r>
322</w:document>"#;
323        let mut parser = EventReader::new(c.as_bytes());
324        let run = Run::read(&mut parser, &[]).unwrap();
325        assert_eq!(
326            run,
327            Run {
328                children: vec![],
329                run_property: RunProperty {
330                    bold: Some(Bold::new().disable()),
331                    bold_cs: Some(BoldCs::new().disable()),
332                    italic: Some(Italic::new()),
333                    italic_cs: Some(ItalicCs::new()),
334                    ..RunProperty::default()
335                },
336            }
337        );
338    }
339
340    #[test]
341    fn test_read_fit_text() {
342        let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
343  <w:r><w:rPr>
344    <w:fitText w:val="840" w:id="1266434317"/>
345  </w:rPr></w:r>
346</w:document>"#;
347        let mut parser = EventReader::new(c.as_bytes());
348        let run = Run::read(&mut parser, &[]).unwrap();
349        assert_eq!(
350            run,
351            Run {
352                children: vec![],
353                run_property: RunProperty {
354                    fit_text: Some(FitText::new(840).id(1266434317)),
355                    ..RunProperty::default()
356                },
357            }
358        );
359    }
360}