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_event();
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::CarriageReturn => {
98                                    run = run.add_carriage_return();
99                                }
100                                XMLElement::Drawing => {
101                                    if let Ok(drawing) = Drawing::read(r, &attributes) {
102                                        run = run.add_drawing(drawing);
103                                    }
104                                }
105                                XMLElement::FieldChar => {
106                                    if let Ok(f) = read_field_char(&attributes) {
107                                        run.children.push(RunChild::FieldChar(f));
108                                    }
109                                }
110                                XMLElement::InstrText => loop {
111                                    let e = r.next_event();
112                                    match e {
113                                        Ok(XmlEvent::Characters(c)) => {
114                                            run.children.push(RunChild::InstrTextString(c));
115                                            break;
116                                        }
117                                        Ok(XmlEvent::EndElement { name, .. }) => {
118                                            let e = XMLElement::from_str(&name.local_name).unwrap();
119                                            match e {
120                                                XMLElement::Run => {
121                                                    return Ok(run);
122                                                }
123                                                _ => {}
124                                            }
125                                        }
126                                        Err(_) => return Err(ReaderError::XMLReadError),
127                                        _ => {}
128                                    }
129                                },
130                                _ => {}
131                            }
132                        }
133                        Some("mc") => {
134                            let e = McXMLElement::from_str(&name.local_name).unwrap();
135                            match e {
136                                McXMLElement::Fallback => {
137                                    let _ = McFallback::read(r, &attributes)?;
138                                }
139                                _ => {}
140                            }
141                        }
142                        Some("v") => {
143                            let e = VXMLElement::from_str(&name.local_name).unwrap();
144                            match e {
145                                // Experimental For now support only imageData in shape
146                                VXMLElement::Shape => {
147                                    if let Ok(shape) = Shape::read(r, &attributes) {
148                                        run.children.push(RunChild::Shape(Box::new(shape)));
149                                    }
150                                }
151                                _ => {}
152                            }
153                        }
154                        _ => {}
155                    };
156                }
157                Ok(XmlEvent::Characters(c)) => match text_state {
158                    TextState::Delete => {
159                        run = run.add_delete_text_without_escape(replace_escaped(&c));
160                    }
161                    TextState::Text => {
162                        run = run.add_text_without_escape(replace_escaped(&c));
163                    }
164                    _ => {}
165                },
166                Ok(XmlEvent::Whitespace(c)) => match text_state {
167                    TextState::Delete => {
168                        run = run.add_delete_text_without_escape(replace_escaped(&c));
169                    }
170                    TextState::Text => {
171                        run = run.add_text_without_escape(replace_escaped(&c));
172                    }
173                    _ => {}
174                },
175                Ok(XmlEvent::EndElement { name, .. }) => {
176                    let e = XMLElement::from_str(&name.local_name).unwrap();
177                    match e {
178                        XMLElement::Run => {
179                            return Ok(run);
180                        }
181                        XMLElement::DeleteText | XMLElement::Text => text_state = TextState::Idle,
182                        _ => {}
183                    }
184                }
185                Err(_) => return Err(ReaderError::XMLReadError),
186                _ => {}
187            }
188        }
189    }
190}
191
192#[cfg(test)]
193mod tests {
194
195    use super::*;
196    #[cfg(test)]
197    use pretty_assertions::assert_eq;
198
199    #[test]
200    fn test_read_size_color() {
201        let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
202  <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>
203</w:document>"#;
204        let mut parser = EventReader::new(c.as_bytes());
205        let run = Run::read(&mut parser, &[]).unwrap();
206        assert_eq!(
207            run,
208            Run {
209                children: vec![RunChild::Text(Text::new("H"))],
210                run_property: RunProperty {
211                    sz: Some(Sz::new(30)),
212                    sz_cs: Some(SzCs::new(30)),
213                    color: Some(Color::new("C9211E")),
214                    ..RunProperty::default()
215                },
216            }
217        );
218    }
219
220    #[test]
221    fn test_read_theme_color() {
222        // The reader must pick attributes by name (not position) and round-trip
223        // w:themeColor / w:themeShade alongside the w:val fallback.
224        let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
225  <w:r><w:rPr><w:color w:val="2E74B5" w:themeColor="accent1" w:themeShade="BF"/></w:rPr><w:t>H</w:t></w:r>
226</w:document>"#;
227        let mut parser = EventReader::new(c.as_bytes());
228        let run = Run::read(&mut parser, &[]).unwrap();
229        assert_eq!(
230            run,
231            Run {
232                children: vec![RunChild::Text(Text::new("H"))],
233                run_property: RunProperty {
234                    color: Some(
235                        Color::new("2E74B5")
236                            .theme_color(crate::ThemeColor::Accent1)
237                            .theme_shade("BF")
238                    ),
239                    ..RunProperty::default()
240                },
241            }
242        );
243    }
244
245    #[test]
246    fn test_read_tab() {
247        let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
248  <w:r><w:tab /></w:r>
249</w:document>"#;
250        let mut parser = EventReader::new(c.as_bytes());
251        let run = Run::read(&mut parser, &[]).unwrap();
252        assert_eq!(
253            run,
254            Run {
255                children: vec![RunChild::Tab(Tab::new())],
256                run_property: RunProperty::default(),
257            }
258        );
259    }
260
261    #[test]
262    fn test_read_br() {
263        let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
264  <w:r><w:br w:type="page" /></w:r>
265</w:document>"#;
266        let mut parser = EventReader::new(c.as_bytes());
267        let run = Run::read(&mut parser, &[]).unwrap();
268        assert_eq!(
269            run,
270            Run {
271                children: vec![RunChild::Break(Break::new(BreakType::Page))],
272                run_property: RunProperty::default(),
273            }
274        );
275    }
276
277    #[test]
278    fn test_read_empty_br() {
279        let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
280  <w:r><w:br /></w:r>
281</w:document>"#;
282        let mut parser = EventReader::new(c.as_bytes());
283        let run = Run::read(&mut parser, &[]).unwrap();
284        assert_eq!(
285            run,
286            Run {
287                children: vec![RunChild::Break(Break::new(BreakType::TextWrapping))],
288                run_property: RunProperty::default(),
289            }
290        );
291    }
292
293    #[test]
294    fn test_read_cr() {
295        let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
296  <w:r><w:cr /></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![RunChild::CarriageReturn(CarriageReturn::new())],
304                run_property: RunProperty::default(),
305            }
306        );
307    }
308
309    #[test]
310    fn test_read_italic_false() {
311        let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
312  <w:r><w:rPr>
313    <w:b w:val="true"/>
314    <w:i w:val="false"/>
315  </w:rPr></w:r>
316</w:document>"#;
317        let mut parser = EventReader::new(c.as_bytes());
318        let run = Run::read(&mut parser, &[]).unwrap();
319        assert_eq!(
320            run,
321            Run {
322                children: vec![],
323                run_property: RunProperty {
324                    bold: Some(Bold::new()),
325                    bold_cs: Some(BoldCs::new()),
326                    italic: Some(Italic::new().disable()),
327                    italic_cs: Some(ItalicCs::new().disable()),
328                    ..RunProperty::default()
329                },
330            }
331        );
332    }
333
334    #[test]
335    fn test_read_italic_0() {
336        let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
337  <w:r><w:rPr>
338    <w:b w:val="1"/>
339    <w:i w:val="0"/>
340  </w:rPr></w:r>
341</w:document>"#;
342        let mut parser = EventReader::new(c.as_bytes());
343        let run = Run::read(&mut parser, &[]).unwrap();
344        assert_eq!(
345            run,
346            Run {
347                children: vec![],
348                run_property: RunProperty {
349                    bold: Some(Bold::new()),
350                    bold_cs: Some(BoldCs::new()),
351                    italic: Some(Italic::new().disable()),
352                    italic_cs: Some(ItalicCs::new().disable()),
353                    ..RunProperty::default()
354                },
355            }
356        );
357    }
358
359    #[test]
360    fn test_read_on_off_values() {
361        let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
362  <w:r><w:rPr>
363    <w:b w:val="off"/>
364    <w:i w:val="on"/>
365  </w:rPr></w:r>
366</w:document>"#;
367        let mut parser = EventReader::new(c.as_bytes());
368        let run = Run::read(&mut parser, &[]).unwrap();
369        assert_eq!(
370            run,
371            Run {
372                children: vec![],
373                run_property: RunProperty {
374                    bold: Some(Bold::new().disable()),
375                    bold_cs: Some(BoldCs::new().disable()),
376                    italic: Some(Italic::new()),
377                    italic_cs: Some(ItalicCs::new()),
378                    ..RunProperty::default()
379                },
380            }
381        );
382    }
383
384    #[test]
385    fn test_read_fit_text() {
386        let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
387  <w:r><w:rPr>
388    <w:fitText w:val="840" w:id="1266434317"/>
389  </w:rPr></w:r>
390</w:document>"#;
391        let mut parser = EventReader::new(c.as_bytes());
392        let run = Run::read(&mut parser, &[]).unwrap();
393        assert_eq!(
394            run,
395            Run {
396                children: vec![],
397                run_property: RunProperty {
398                    fit_text: Some(FitText::new(840).id(1266434317)),
399                    ..RunProperty::default()
400                },
401            }
402        );
403    }
404}