docx_rs/reader/
paragraph.rs

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