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_event();
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::MoveFrom => {
47 let mf = MoveFrom::read(r, &attributes)?;
48 p = p.add_move_from(mf);
49 continue;
50 }
51 XMLElement::MoveTo => {
52 let mt = MoveTo::read(r, &attributes)?;
53 p = p.add_move_to(mt);
54 continue;
55 }
56 XMLElement::BookmarkStart => {
57 let s = BookmarkStart::read(r, &attributes)?;
58 p = p.add_bookmark_start(s.id, s.name);
59 continue;
60 }
61 XMLElement::BookmarkEnd => {
62 let e = BookmarkEnd::read(r, &attributes)?;
63 p = p.add_bookmark_end(e.id);
64 continue;
65 }
66 XMLElement::CommentRangeStart => {
67 if let Some(id) = read(&attributes, "id") {
68 if let Ok(id) = usize::from_str(&id) {
69 let comment = Comment::new(id);
70 p = p.add_comment_start(comment);
71 }
72 }
73 continue;
74 }
75 XMLElement::CommentRangeEnd => {
76 if let Some(id) = read(&attributes, "id") {
77 if let Ok(id) = usize::from_str(&id) {
78 p = p.add_comment_end(id);
79 }
80 }
81 continue;
82 }
83 XMLElement::ParagraphProperty => {
85 if let Ok(pr) = ParagraphProperty::read(r, &attributes) {
86 p.has_numbering = pr.numbering_property.is_some();
87 p.property = pr;
88 }
89 continue;
90 }
91 _ => {}
92 }
93 }
94 Ok(XmlEvent::EndElement { name, .. }) => {
95 let e = XMLElement::from_str(&name.local_name).unwrap();
96 if e == XMLElement::Paragraph {
97 return Ok(p);
98 }
99 }
100 Err(_) => return Err(ReaderError::XMLReadError),
101 _ => {}
102 }
103 }
104 }
105}
106
107#[cfg(test)]
108mod tests {
109
110 use super::*;
111 use crate::types::*;
112 #[cfg(test)]
113 use pretty_assertions::assert_eq;
114
115 #[test]
116 fn test_read_indent() {
117 let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
118 <w:p>
119 <w:pPr>
120 <w:ind w:left="1470" w:right="1270" w:hanging="0"/>
121 <w:rPr></w:rPr>
122 </w:pPr>
123 <w:r>
124 <w:rPr></w:rPr>
125 <w:t>a</w:t>
126 </w:r>
127 </w:p>
128</w:document>"#;
129 let mut parser = EventReader::new(c.as_bytes());
130 let p = Paragraph::read(&mut parser, &[]).unwrap();
131
132 assert_eq!(
133 p,
134 Paragraph {
135 id: "12345678".to_owned(),
136 children: vec![ParagraphChild::Run(Box::new(Run::new().add_text("a")))],
137 property: ParagraphProperty {
138 run_property: RunProperty::new(),
139 style: None,
140 numbering_property: None,
141 alignment: None,
142 indent: Some(Indent::new(
143 Some(1470),
144 Some(SpecialIndentType::Hanging(0)),
145 Some(1270),
146 None,
147 )),
148 line_spacing: None,
149 ..Default::default()
150 },
151 has_numbering: false,
152 }
153 );
154 }
155
156 #[test]
157 fn test_read_indent_start_chars() {
158 let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
159 <w:p>
160 <w:pPr>
161 <w:ind w:startChars="100" />
162 <w:rPr></w:rPr>
163 </w:pPr>
164 <w:r>
165 <w:rPr></w:rPr>
166 <w:t>a</w:t>
167 </w:r>
168 </w:p>
169</w:document>"#;
170 let mut parser = EventReader::new(c.as_bytes());
171 let p = Paragraph::read(&mut parser, &[]).unwrap();
172
173 assert_eq!(
174 p,
175 Paragraph {
176 id: "12345678".to_owned(),
177 children: vec![ParagraphChild::Run(Box::new(Run::new().add_text("a")))],
178 property: ParagraphProperty {
179 run_property: RunProperty::new(),
180 style: None,
181 numbering_property: None,
182 alignment: None,
183 indent: Some(Indent::new(None, None, None, Some(100))),
184 line_spacing: None,
185 ..Default::default()
186 },
187 has_numbering: false,
188 }
189 );
190 }
191
192 #[test]
193 fn test_read_jc() {
194 let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
195 <w:p>
196 <w:pPr>
197 <w:jc w:val="left"/>
198 </w:pPr>
199 </w:p>
200</w:document>"#;
201 let mut parser = EventReader::new(c.as_bytes());
202 let p = Paragraph::read(&mut parser, &[]).unwrap();
203
204 assert_eq!(
205 p,
206 Paragraph {
207 id: "12345678".to_owned(),
208 children: vec![],
209 property: ParagraphProperty {
210 run_property: RunProperty::new(),
211 style: None,
212 numbering_property: None,
213 alignment: Some(Justification::new(AlignmentType::Left.to_string())),
214 indent: None,
215 line_spacing: None,
216 ..Default::default()
217 },
218 has_numbering: false,
219 }
220 );
221 }
222
223 #[test]
224 fn test_read_numbering() {
225 let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
226 <w:p>
227 <w:pPr>
228 <w:numPr>
229 <w:ilvl w:val="0"/>
230 <w:numId w:val="1"/>
231 </w:numPr>
232 </w:pPr>
233 </w:p>
234</w:document>"#;
235 let mut parser = EventReader::new(c.as_bytes());
236 let p = Paragraph::read(&mut parser, &[]).unwrap();
237 assert_eq!(
238 p,
239 Paragraph {
240 id: "12345678".to_owned(),
241 children: vec![],
242 property: ParagraphProperty {
243 run_property: RunProperty::new(),
244 style: None,
245 numbering_property: Some(
246 NumberingProperty::new().add_num(NumberingId::new(1), IndentLevel::new(0),)
247 ),
248 alignment: None,
249 indent: None,
250 line_spacing: None,
251 ..Default::default()
252 },
253 has_numbering: true,
254 }
255 );
256 }
257
258 #[test]
259 fn test_read_insert() {
260 let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
261 <w:p>
262 <w:ins w:id="0" w:author="unknown" w:date="2019-11-15T14:19:04Z">
263 <w:r>
264 <w:rPr></w:rPr>
265 <w:t>W</w:t>
266 </w:r>
267 </w:ins>
268 </w:p>
269</w:document>"#;
270 let mut parser = EventReader::new(c.as_bytes());
271 let p = Paragraph::read(&mut parser, &[]).unwrap();
272 assert_eq!(
273 p,
274 Paragraph {
275 id: "12345678".to_owned(),
276 children: vec![ParagraphChild::Insert(
277 Insert::new(Run::new().add_text("W"))
278 .author("unknown")
279 .date("2019-11-15T14:19:04Z")
280 )],
281 property: ParagraphProperty {
282 run_property: RunProperty::new(),
283 style: None,
284 numbering_property: None,
285 alignment: None,
286 indent: None,
287 line_spacing: None,
288 ..Default::default()
289 },
290 has_numbering: false,
291 }
292 );
293 }
294
295 #[test]
296 fn test_read_delete() {
297 let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
298 <w:p>
299 <w:del w:id="3" w:author="unknown" w:date="2019-11-15T14:19:04Z">
300 <w:r>
301 <w:rPr></w:rPr>
302 <w:delText xml:space="preserve">Hello </w:delText>
303 </w:r>
304 </w:del>
305 </w:p>
306</w:document>"#;
307 let mut parser = EventReader::new(c.as_bytes());
308 let p = Paragraph::read(&mut parser, &[]).unwrap();
309
310 assert_eq!(
311 p,
312 Paragraph {
313 id: "12345678".to_owned(),
314 children: vec![ParagraphChild::Delete(
315 Delete::new()
316 .add_run(Run::new().add_delete_text("Hello "))
317 .author("unknown")
318 .date("2019-11-15T14:19:04Z")
319 )],
320 property: ParagraphProperty {
321 run_property: RunProperty::new(),
322 style: None,
323 numbering_property: None,
324 alignment: None,
325 indent: None,
326 line_spacing: None,
327 ..Default::default()
328 },
329 has_numbering: false,
330 }
331 );
332 }
333
334 #[test]
335 fn test_read_bookmark() {
336 let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
337 <w:p>
338 <w:bookmarkStart w:id="0" w:name="ABCD-1234"/>
339 <w:r>
340 <w:rPr></w:rPr>
341 <w:t>Bookmarked</w:t>
342 </w:r>
343 <w:bookmarkEnd w:id="0"/>
344 </w:p>
345</w:document>"#;
346 let mut parser = EventReader::new(c.as_bytes());
347 let p = Paragraph::read(&mut parser, &[]).unwrap();
348
349 assert_eq!(
350 p,
351 Paragraph {
352 id: "12345678".to_owned(),
353 children: vec![
354 ParagraphChild::BookmarkStart(BookmarkStart::new(0, "ABCD-1234")),
355 ParagraphChild::Run(Box::new(Run::new().add_text("Bookmarked"))),
356 ParagraphChild::BookmarkEnd(BookmarkEnd::new(0)),
357 ],
358 property: ParagraphProperty {
359 run_property: RunProperty::new(),
360 style: None,
361 numbering_property: None,
362 alignment: None,
363 indent: None,
364 line_spacing: None,
365 ..Default::default()
366 },
367 has_numbering: false,
368 }
369 );
370 }
371
372 #[test]
373 fn test_read_two_insert() {
374 let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
375 <w:p>
376 <w:ins w:id="0" w:author="unknown" w:date="2019-11-15T14:19:04Z">
377 <w:r>
378 <w:rPr></w:rPr>
379 <w:t>W</w:t>
380 </w:r>
381 </w:ins>
382 <w:ins w:id="0" w:author="unknown" w:date="2019-11-15T14:19:04Z">
383 <w:r>
384 <w:rPr></w:rPr>
385 <w:t>H</w:t>
386 </w:r>
387 </w:ins>
388 </w:p>
389</w:document>"#;
390 let mut parser = EventReader::new(c.as_bytes());
391 let p = Paragraph::read(&mut parser, &[]).unwrap();
392 assert_eq!(
393 p,
394 Paragraph {
395 id: "12345678".to_owned(),
396 children: vec![
397 ParagraphChild::Insert(
398 Insert::new(Run::new().add_text("W"))
399 .author("unknown")
400 .date("2019-11-15T14:19:04Z")
401 ),
402 ParagraphChild::Insert(
403 Insert::new(Run::new().add_text("H"))
404 .author("unknown")
405 .date("2019-11-15T14:19:04Z")
406 )
407 ],
408 property: ParagraphProperty {
409 run_property: RunProperty::new(),
410 style: None,
411 numbering_property: None,
412 alignment: None,
413 indent: None,
414 line_spacing: None,
415 ..Default::default()
416 },
417 has_numbering: false,
418 }
419 );
420 }
421
422 #[test]
423 fn test_read_two_run_in_insert() {
424 let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
425 <w:p>
426 <w:ins w:id="0" w:author="unknown" w:date="2019-11-15T14:19:04Z">
427 <w:r>
428 <w:rPr></w:rPr>
429 <w:t>W</w:t>
430 </w:r>
431 <w:r>
432 <w:rPr></w:rPr>
433 <w:t>H</w:t>
434 </w:r>
435 </w:ins>
436 </w:p>
437</w:document>"#;
438 let mut parser = EventReader::new(c.as_bytes());
439 let p = Paragraph::read(&mut parser, &[]).unwrap();
440 assert_eq!(
441 p,
442 Paragraph {
443 id: "12345678".to_owned(),
444 children: vec![ParagraphChild::Insert(
445 Insert::new(Run::new().add_text("W"))
446 .author("unknown")
447 .date("2019-11-15T14:19:04Z")
448 .add_run(Run::new().add_text("H")),
449 )],
450 property: ParagraphProperty {
451 run_property: RunProperty::new(),
452 style: None,
453 numbering_property: None,
454 alignment: None,
455 indent: None,
456 line_spacing: None,
457 ..Default::default()
458 },
459 has_numbering: false,
460 }
461 );
462 }
463
464 #[test]
465 fn test_read_move_from_to() {
466 let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
467 <w:p>
468 <w:moveFrom w:id="1" w:author="user1" w:date="2024-01-01T00:00:00Z">
469 <w:r>
470 <w:rPr></w:rPr>
471 <w:t>moved</w:t>
472 </w:r>
473 </w:moveFrom>
474 <w:moveTo w:id="2" w:author="user1" w:date="2024-01-01T00:00:00Z">
475 <w:r>
476 <w:rPr></w:rPr>
477 <w:t>moved</w:t>
478 </w:r>
479 </w:moveTo>
480 </w:p>
481</w:document>"#;
482 let mut parser = EventReader::new(c.as_bytes());
483 let p = Paragraph::read(&mut parser, &[]).unwrap();
484 assert_eq!(
485 p,
486 Paragraph {
487 id: "12345678".to_owned(),
488 children: vec![
489 ParagraphChild::MoveFrom(
490 MoveFrom::new()
491 .add_run(Run::new().add_text("moved"))
492 .author("user1")
493 .date("2024-01-01T00:00:00Z")
494 ),
495 ParagraphChild::MoveTo(
496 MoveTo::new(Run::new().add_text("moved"))
497 .author("user1")
498 .date("2024-01-01T00:00:00Z")
499 ),
500 ],
501 property: ParagraphProperty {
502 run_property: RunProperty::new(),
503 style: None,
504 numbering_property: None,
505 alignment: None,
506 indent: None,
507 line_spacing: None,
508 ..Default::default()
509 },
510 has_numbering: false,
511 }
512 );
513 assert_eq!(p.raw_text(), "moved");
515 }
516
517 #[test]
518 fn test_read_move_from_nested_in_insert() {
519 let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
521 <w:p>
522 <w:ins w:id="0" w:author="unknown" w:date="2024-01-01T00:00:00Z">
523 <w:moveFrom w:id="1" w:author="unknown" w:date="2024-01-01T00:00:00Z">
524 <w:r>
525 <w:rPr></w:rPr>
526 <w:t>ghost</w:t>
527 </w:r>
528 </w:moveFrom>
529 <w:r>
530 <w:rPr></w:rPr>
531 <w:t>live</w:t>
532 </w:r>
533 </w:ins>
534 </w:p>
535</w:document>"#;
536 let mut parser = EventReader::new(c.as_bytes());
537 let p = Paragraph::read(&mut parser, &[]).unwrap();
538 assert_eq!(p.raw_text(), "live");
540 }
541
542 #[test]
543 fn test_read_move_from_nested_in_move_to() {
544 let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
546 <w:p>
547 <w:moveTo w:id="2" w:author="unknown" w:date="2024-01-01T00:00:00Z">
548 <w:moveFrom w:id="1" w:author="unknown" w:date="2024-01-01T00:00:00Z">
549 <w:r>
550 <w:rPr></w:rPr>
551 <w:t>ghost</w:t>
552 </w:r>
553 </w:moveFrom>
554 <w:r>
555 <w:rPr></w:rPr>
556 <w:t>live</w:t>
557 </w:r>
558 </w:moveTo>
559 </w:p>
560</w:document>"#;
561 let mut parser = EventReader::new(c.as_bytes());
562 let p = Paragraph::read(&mut parser, &[]).unwrap();
563 assert_eq!(p.raw_text(), "live");
565 }
566}