Skip to main content

docx_rs/reader/
paragraph.rs

1use std::io::Read;
2use std::str::FromStr;
3
4use super::*;
5
6use super::attributes::*;
7
8impl ElementReader for Paragraph {
9    fn read<R: Read>(
10        r: &mut EventReader<R>,
11        attrs: &[OwnedAttribute],
12    ) -> Result<Self, ReaderError> {
13        let mut p = Paragraph::new();
14        if let Some(para_id) = read(attrs, "paraId") {
15            p = p.id(para_id);
16        }
17        loop {
18            let e = r.next();
19            match e {
20                Ok(XmlEvent::StartElement {
21                    attributes, name, ..
22                }) => {
23                    let e = XMLElement::from_str(&name.local_name).unwrap();
24
25                    match e {
26                        XMLElement::Run => {
27                            let run = Run::read(r, &attributes)?;
28                            p = p.add_run(run);
29                            continue;
30                        }
31                        XMLElement::Hyperlink => {
32                            let link = Hyperlink::read(r, &attributes)?;
33                            p = p.add_hyperlink(link);
34                            continue;
35                        }
36                        XMLElement::Insert => {
37                            let ins = Insert::read(r, &attributes)?;
38                            p = p.add_insert(ins);
39                            continue;
40                        }
41                        XMLElement::Delete => {
42                            let del = Delete::read(r, &attributes)?;
43                            p = p.add_delete(del);
44                            continue;
45                        }
46                        XMLElement::BookmarkStart => {
47                            let s = BookmarkStart::read(r, &attributes)?;
48                            p = p.add_bookmark_start(s.id, s.name);
49                            continue;
50                        }
51                        XMLElement::BookmarkEnd => {
52                            let e = BookmarkEnd::read(r, &attributes)?;
53                            p = p.add_bookmark_end(e.id);
54                            continue;
55                        }
56                        XMLElement::CommentRangeStart => {
57                            if let Some(id) = read(&attributes, "id") {
58                                if let Ok(id) = usize::from_str(&id) {
59                                    let comment = Comment::new(id);
60                                    p = p.add_comment_start(comment);
61                                }
62                            }
63                            continue;
64                        }
65                        XMLElement::CommentRangeEnd => {
66                            if let Some(id) = read(&attributes, "id") {
67                                if let Ok(id) = usize::from_str(&id) {
68                                    p = p.add_comment_end(id);
69                                }
70                            }
71                            continue;
72                        }
73                        // pPr
74                        XMLElement::ParagraphProperty => {
75                            if let Ok(pr) = ParagraphProperty::read(r, &attributes) {
76                                p.has_numbering = pr.numbering_property.is_some();
77                                p.property = pr;
78                            }
79                            continue;
80                        }
81                        _ => {}
82                    }
83                }
84                Ok(XmlEvent::EndElement { name, .. }) => {
85                    let e = XMLElement::from_str(&name.local_name).unwrap();
86                    if e == XMLElement::Paragraph {
87                        return Ok(p);
88                    }
89                }
90                Err(_) => return Err(ReaderError::XMLReadError),
91                _ => {}
92            }
93        }
94    }
95}
96
97#[cfg(test)]
98mod tests {
99
100    use super::*;
101    use crate::types::*;
102    #[cfg(test)]
103    use pretty_assertions::assert_eq;
104
105    #[test]
106    fn test_read_indent() {
107        let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
108    <w:p>
109        <w:pPr>
110            <w:ind w:left="1470" w:right="1270" w:hanging="0"/>
111            <w:rPr></w:rPr>
112        </w:pPr>
113        <w:r>
114            <w:rPr></w:rPr>
115            <w:t>a</w:t>
116        </w:r>
117    </w:p>
118</w:document>"#;
119        let mut parser = EventReader::new(c.as_bytes());
120        let p = Paragraph::read(&mut parser, &[]).unwrap();
121
122        assert_eq!(
123            p,
124            Paragraph {
125                id: "12345678".to_owned(),
126                children: vec![ParagraphChild::Run(Box::new(Run::new().add_text("a")))],
127                property: ParagraphProperty {
128                    run_property: RunProperty::new(),
129                    style: None,
130                    numbering_property: None,
131                    alignment: None,
132                    indent: Some(Indent::new(
133                        Some(1470),
134                        Some(SpecialIndentType::Hanging(0)),
135                        Some(1270),
136                        None,
137                    )),
138                    line_spacing: None,
139                    ..Default::default()
140                },
141                has_numbering: false,
142            }
143        );
144    }
145
146    #[test]
147    fn test_read_indent_start_chars() {
148        let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
149    <w:p>
150        <w:pPr>
151            <w:ind w:startChars="100" />
152            <w:rPr></w:rPr>
153        </w:pPr>
154        <w:r>
155            <w:rPr></w:rPr>
156            <w:t>a</w:t>
157        </w:r>
158    </w:p>
159</w:document>"#;
160        let mut parser = EventReader::new(c.as_bytes());
161        let p = Paragraph::read(&mut parser, &[]).unwrap();
162
163        assert_eq!(
164            p,
165            Paragraph {
166                id: "12345678".to_owned(),
167                children: vec![ParagraphChild::Run(Box::new(Run::new().add_text("a")))],
168                property: ParagraphProperty {
169                    run_property: RunProperty::new(),
170                    style: None,
171                    numbering_property: None,
172                    alignment: None,
173                    indent: Some(Indent::new(None, None, None, Some(100))),
174                    line_spacing: None,
175                    ..Default::default()
176                },
177                has_numbering: false,
178            }
179        );
180    }
181
182    #[test]
183    fn test_read_jc() {
184        let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
185    <w:p>
186        <w:pPr>
187            <w:jc w:val="left"/>
188        </w:pPr>
189    </w:p>
190</w:document>"#;
191        let mut parser = EventReader::new(c.as_bytes());
192        let p = Paragraph::read(&mut parser, &[]).unwrap();
193
194        assert_eq!(
195            p,
196            Paragraph {
197                id: "12345678".to_owned(),
198                children: vec![],
199                property: ParagraphProperty {
200                    run_property: RunProperty::new(),
201                    style: None,
202                    numbering_property: None,
203                    alignment: Some(Justification::new(AlignmentType::Left.to_string())),
204                    indent: None,
205                    line_spacing: None,
206                    ..Default::default()
207                },
208                has_numbering: false,
209            }
210        );
211    }
212
213    #[test]
214    fn test_read_numbering() {
215        let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
216    <w:p>
217        <w:pPr>
218            <w:numPr>
219                <w:ilvl w:val="0"/>
220                <w:numId w:val="1"/>
221            </w:numPr>
222        </w:pPr>
223    </w:p>
224</w:document>"#;
225        let mut parser = EventReader::new(c.as_bytes());
226        let p = Paragraph::read(&mut parser, &[]).unwrap();
227        assert_eq!(
228            p,
229            Paragraph {
230                id: "12345678".to_owned(),
231                children: vec![],
232                property: ParagraphProperty {
233                    run_property: RunProperty::new(),
234                    style: None,
235                    numbering_property: Some(
236                        NumberingProperty::new().add_num(NumberingId::new(1), IndentLevel::new(0),)
237                    ),
238                    alignment: None,
239                    indent: None,
240                    line_spacing: None,
241                    ..Default::default()
242                },
243                has_numbering: true,
244            }
245        );
246    }
247
248    #[test]
249    fn test_read_insert() {
250        let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
251    <w:p>
252        <w:ins w:id="0" w:author="unknown" w:date="2019-11-15T14:19:04Z">
253            <w:r>
254                <w:rPr></w:rPr>
255                <w:t>W</w:t>
256            </w:r>
257        </w:ins>
258    </w:p>
259</w:document>"#;
260        let mut parser = EventReader::new(c.as_bytes());
261        let p = Paragraph::read(&mut parser, &[]).unwrap();
262        assert_eq!(
263            p,
264            Paragraph {
265                id: "12345678".to_owned(),
266                children: vec![ParagraphChild::Insert(
267                    Insert::new(Run::new().add_text("W"))
268                        .author("unknown")
269                        .date("2019-11-15T14:19:04Z")
270                )],
271                property: ParagraphProperty {
272                    run_property: RunProperty::new(),
273                    style: None,
274                    numbering_property: None,
275                    alignment: None,
276                    indent: None,
277                    line_spacing: None,
278                    ..Default::default()
279                },
280                has_numbering: false,
281            }
282        );
283    }
284
285    #[test]
286    fn test_read_delete() {
287        let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
288    <w:p>
289        <w:del w:id="3" w:author="unknown" w:date="2019-11-15T14:19:04Z">
290            <w:r>
291                <w:rPr></w:rPr>
292                <w:delText xml:space="preserve">Hello </w:delText>
293            </w:r>
294        </w:del>
295    </w:p>
296</w:document>"#;
297        let mut parser = EventReader::new(c.as_bytes());
298        let p = Paragraph::read(&mut parser, &[]).unwrap();
299
300        assert_eq!(
301            p,
302            Paragraph {
303                id: "12345678".to_owned(),
304                children: vec![ParagraphChild::Delete(
305                    Delete::new()
306                        .add_run(Run::new().add_delete_text("Hello "))
307                        .author("unknown")
308                        .date("2019-11-15T14:19:04Z")
309                )],
310                property: ParagraphProperty {
311                    run_property: RunProperty::new(),
312                    style: None,
313                    numbering_property: None,
314                    alignment: None,
315                    indent: None,
316                    line_spacing: None,
317                    ..Default::default()
318                },
319                has_numbering: false,
320            }
321        );
322    }
323
324    #[test]
325    fn test_read_bookmark() {
326        let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
327    <w:p>
328        <w:bookmarkStart w:id="0" w:name="ABCD-1234"/>
329            <w:r>
330                <w:rPr></w:rPr>
331                <w:t>Bookmarked</w:t>
332            </w:r>
333        <w:bookmarkEnd w:id="0"/>
334    </w:p>
335</w:document>"#;
336        let mut parser = EventReader::new(c.as_bytes());
337        let p = Paragraph::read(&mut parser, &[]).unwrap();
338
339        assert_eq!(
340            p,
341            Paragraph {
342                id: "12345678".to_owned(),
343                children: vec![
344                    ParagraphChild::BookmarkStart(BookmarkStart::new(0, "ABCD-1234")),
345                    ParagraphChild::Run(Box::new(Run::new().add_text("Bookmarked"))),
346                    ParagraphChild::BookmarkEnd(BookmarkEnd::new(0)),
347                ],
348                property: ParagraphProperty {
349                    run_property: RunProperty::new(),
350                    style: None,
351                    numbering_property: None,
352                    alignment: None,
353                    indent: None,
354                    line_spacing: None,
355                    ..Default::default()
356                },
357                has_numbering: false,
358            }
359        );
360    }
361
362    #[test]
363    fn test_read_two_insert() {
364        let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
365    <w:p>
366        <w:ins w:id="0" w:author="unknown" w:date="2019-11-15T14:19:04Z">
367            <w:r>
368                <w:rPr></w:rPr>
369                <w:t>W</w:t>
370            </w:r>
371        </w:ins>
372        <w:ins w:id="0" w:author="unknown" w:date="2019-11-15T14:19:04Z">
373            <w:r>
374                <w:rPr></w:rPr>
375                <w:t>H</w:t>
376            </w:r>
377        </w:ins>
378    </w:p>
379</w:document>"#;
380        let mut parser = EventReader::new(c.as_bytes());
381        let p = Paragraph::read(&mut parser, &[]).unwrap();
382        assert_eq!(
383            p,
384            Paragraph {
385                id: "12345678".to_owned(),
386                children: vec![
387                    ParagraphChild::Insert(
388                        Insert::new(Run::new().add_text("W"))
389                            .author("unknown")
390                            .date("2019-11-15T14:19:04Z")
391                    ),
392                    ParagraphChild::Insert(
393                        Insert::new(Run::new().add_text("H"))
394                            .author("unknown")
395                            .date("2019-11-15T14:19:04Z")
396                    )
397                ],
398                property: ParagraphProperty {
399                    run_property: RunProperty::new(),
400                    style: None,
401                    numbering_property: None,
402                    alignment: None,
403                    indent: None,
404                    line_spacing: None,
405                    ..Default::default()
406                },
407                has_numbering: false,
408            }
409        );
410    }
411
412    #[test]
413    fn test_read_two_run_in_insert() {
414        let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
415    <w:p>
416        <w:ins w:id="0" w:author="unknown" w:date="2019-11-15T14:19:04Z">
417            <w:r>
418                <w:rPr></w:rPr>
419                <w:t>W</w:t>
420            </w:r>
421            <w:r>
422                <w:rPr></w:rPr>
423                <w:t>H</w:t>
424            </w:r>
425        </w:ins>
426    </w:p>
427</w:document>"#;
428        let mut parser = EventReader::new(c.as_bytes());
429        let p = Paragraph::read(&mut parser, &[]).unwrap();
430        assert_eq!(
431            p,
432            Paragraph {
433                id: "12345678".to_owned(),
434                children: vec![ParagraphChild::Insert(
435                    Insert::new(Run::new().add_text("W"))
436                        .author("unknown")
437                        .date("2019-11-15T14:19:04Z")
438                        .add_run(Run::new().add_text("H")),
439                )],
440                property: ParagraphProperty {
441                    run_property: RunProperty::new(),
442                    style: None,
443                    numbering_property: None,
444                    alignment: None,
445                    indent: None,
446                    line_spacing: None,
447                    ..Default::default()
448                },
449                has_numbering: false,
450            }
451        );
452    }
453}