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_paragraph_borders() {
225 let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
226 <w:p>
227 <w:pPr>
228 <w:pBdr>
229 <w:top w:val="double" w:sz="24" w:space="1" w:color="622423"/>
230 <w:between w:val="dashed" w:sz="8" w:space="2" w:color="auto"/>
231 </w:pBdr>
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
238 assert_eq!(
239 p.property.borders,
240 Some(
241 ParagraphBorders::with_empty()
242 .set(
243 ParagraphBorder::new(ParagraphBorderPosition::Top)
244 .val(BorderType::Double)
245 .size(24)
246 .space(1)
247 .color("622423"),
248 )
249 .set(
250 ParagraphBorder::new(ParagraphBorderPosition::Between)
251 .val(BorderType::Dashed)
252 .size(8)
253 .space(2)
254 .color("auto"),
255 ),
256 )
257 );
258 }
259
260 #[test]
261 fn test_read_numbering() {
262 let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
263 <w:p>
264 <w:pPr>
265 <w:numPr>
266 <w:ilvl w:val="0"/>
267 <w:numId w:val="1"/>
268 </w:numPr>
269 </w:pPr>
270 </w:p>
271</w:document>"#;
272 let mut parser = EventReader::new(c.as_bytes());
273 let p = Paragraph::read(&mut parser, &[]).unwrap();
274 assert_eq!(
275 p,
276 Paragraph {
277 id: "12345678".to_owned(),
278 children: vec![],
279 property: ParagraphProperty {
280 run_property: RunProperty::new(),
281 style: None,
282 numbering_property: Some(
283 NumberingProperty::new().add_num(NumberingId::new(1), IndentLevel::new(0),)
284 ),
285 alignment: None,
286 indent: None,
287 line_spacing: None,
288 ..Default::default()
289 },
290 has_numbering: true,
291 }
292 );
293 }
294
295 #[test]
296 fn test_read_insert() {
297 let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
298 <w:p>
299 <w:ins w:id="0" w:author="unknown" w:date="2019-11-15T14:19:04Z">
300 <w:r>
301 <w:rPr></w:rPr>
302 <w:t>W</w:t>
303 </w:r>
304 </w:ins>
305 </w:p>
306</w:document>"#;
307 let mut parser = EventReader::new(c.as_bytes());
308 let p = Paragraph::read(&mut parser, &[]).unwrap();
309 assert_eq!(
310 p,
311 Paragraph {
312 id: "12345678".to_owned(),
313 children: vec![ParagraphChild::Insert(
314 Insert::new(Run::new().add_text("W"))
315 .author("unknown")
316 .date("2019-11-15T14:19:04Z")
317 )],
318 property: ParagraphProperty {
319 run_property: RunProperty::new(),
320 style: None,
321 numbering_property: None,
322 alignment: None,
323 indent: None,
324 line_spacing: None,
325 ..Default::default()
326 },
327 has_numbering: false,
328 }
329 );
330 }
331
332 #[test]
333 fn test_read_delete() {
334 let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
335 <w:p>
336 <w:del w:id="3" w:author="unknown" w:date="2019-11-15T14:19:04Z">
337 <w:r>
338 <w:rPr></w:rPr>
339 <w:delText xml:space="preserve">Hello </w:delText>
340 </w:r>
341 </w:del>
342 </w:p>
343</w:document>"#;
344 let mut parser = EventReader::new(c.as_bytes());
345 let p = Paragraph::read(&mut parser, &[]).unwrap();
346
347 assert_eq!(
348 p,
349 Paragraph {
350 id: "12345678".to_owned(),
351 children: vec![ParagraphChild::Delete(
352 Delete::new()
353 .add_run(Run::new().add_delete_text("Hello "))
354 .author("unknown")
355 .date("2019-11-15T14:19:04Z")
356 )],
357 property: ParagraphProperty {
358 run_property: RunProperty::new(),
359 style: None,
360 numbering_property: None,
361 alignment: None,
362 indent: None,
363 line_spacing: None,
364 ..Default::default()
365 },
366 has_numbering: false,
367 }
368 );
369 }
370
371 #[test]
372 fn test_read_bookmark() {
373 let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
374 <w:p>
375 <w:bookmarkStart w:id="0" w:name="ABCD-1234"/>
376 <w:r>
377 <w:rPr></w:rPr>
378 <w:t>Bookmarked</w:t>
379 </w:r>
380 <w:bookmarkEnd w:id="0"/>
381 </w:p>
382</w:document>"#;
383 let mut parser = EventReader::new(c.as_bytes());
384 let p = Paragraph::read(&mut parser, &[]).unwrap();
385
386 assert_eq!(
387 p,
388 Paragraph {
389 id: "12345678".to_owned(),
390 children: vec![
391 ParagraphChild::BookmarkStart(BookmarkStart::new(0, "ABCD-1234")),
392 ParagraphChild::Run(Box::new(Run::new().add_text("Bookmarked"))),
393 ParagraphChild::BookmarkEnd(BookmarkEnd::new(0)),
394 ],
395 property: ParagraphProperty {
396 run_property: RunProperty::new(),
397 style: None,
398 numbering_property: None,
399 alignment: None,
400 indent: None,
401 line_spacing: None,
402 ..Default::default()
403 },
404 has_numbering: false,
405 }
406 );
407 }
408
409 #[test]
410 fn test_read_two_insert() {
411 let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
412 <w:p>
413 <w:ins w:id="0" w:author="unknown" w:date="2019-11-15T14:19:04Z">
414 <w:r>
415 <w:rPr></w:rPr>
416 <w:t>W</w:t>
417 </w:r>
418 </w:ins>
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>H</w:t>
423 </w:r>
424 </w:ins>
425 </w:p>
426</w:document>"#;
427 let mut parser = EventReader::new(c.as_bytes());
428 let p = Paragraph::read(&mut parser, &[]).unwrap();
429 assert_eq!(
430 p,
431 Paragraph {
432 id: "12345678".to_owned(),
433 children: vec![
434 ParagraphChild::Insert(
435 Insert::new(Run::new().add_text("W"))
436 .author("unknown")
437 .date("2019-11-15T14:19:04Z")
438 ),
439 ParagraphChild::Insert(
440 Insert::new(Run::new().add_text("H"))
441 .author("unknown")
442 .date("2019-11-15T14:19:04Z")
443 )
444 ],
445 property: ParagraphProperty {
446 run_property: RunProperty::new(),
447 style: None,
448 numbering_property: None,
449 alignment: None,
450 indent: None,
451 line_spacing: None,
452 ..Default::default()
453 },
454 has_numbering: false,
455 }
456 );
457 }
458
459 #[test]
460 fn test_read_two_run_in_insert() {
461 let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
462 <w:p>
463 <w:ins w:id="0" w:author="unknown" w:date="2019-11-15T14:19:04Z">
464 <w:r>
465 <w:rPr></w:rPr>
466 <w:t>W</w:t>
467 </w:r>
468 <w:r>
469 <w:rPr></w:rPr>
470 <w:t>H</w:t>
471 </w:r>
472 </w:ins>
473 </w:p>
474</w:document>"#;
475 let mut parser = EventReader::new(c.as_bytes());
476 let p = Paragraph::read(&mut parser, &[]).unwrap();
477 assert_eq!(
478 p,
479 Paragraph {
480 id: "12345678".to_owned(),
481 children: vec![ParagraphChild::Insert(
482 Insert::new(Run::new().add_text("W"))
483 .author("unknown")
484 .date("2019-11-15T14:19:04Z")
485 .add_run(Run::new().add_text("H")),
486 )],
487 property: ParagraphProperty {
488 run_property: RunProperty::new(),
489 style: None,
490 numbering_property: None,
491 alignment: None,
492 indent: None,
493 line_spacing: None,
494 ..Default::default()
495 },
496 has_numbering: false,
497 }
498 );
499 }
500
501 #[test]
502 fn test_read_move_from_to() {
503 let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
504 <w:p>
505 <w:moveFrom w:id="1" w:author="user1" w:date="2024-01-01T00:00:00Z">
506 <w:r>
507 <w:rPr></w:rPr>
508 <w:t>moved</w:t>
509 </w:r>
510 </w:moveFrom>
511 <w:moveTo w:id="2" w:author="user1" w:date="2024-01-01T00:00:00Z">
512 <w:r>
513 <w:rPr></w:rPr>
514 <w:t>moved</w:t>
515 </w:r>
516 </w:moveTo>
517 </w:p>
518</w:document>"#;
519 let mut parser = EventReader::new(c.as_bytes());
520 let p = Paragraph::read(&mut parser, &[]).unwrap();
521 assert_eq!(
522 p,
523 Paragraph {
524 id: "12345678".to_owned(),
525 children: vec![
526 ParagraphChild::MoveFrom(
527 MoveFrom::new()
528 .add_run(Run::new().add_text("moved"))
529 .author("user1")
530 .date("2024-01-01T00:00:00Z")
531 ),
532 ParagraphChild::MoveTo(
533 MoveTo::new(Run::new().add_text("moved"))
534 .author("user1")
535 .date("2024-01-01T00:00:00Z")
536 ),
537 ],
538 property: ParagraphProperty {
539 run_property: RunProperty::new(),
540 style: None,
541 numbering_property: None,
542 alignment: None,
543 indent: None,
544 line_spacing: None,
545 ..Default::default()
546 },
547 has_numbering: false,
548 }
549 );
550 assert_eq!(p.raw_text(), "moved");
552 }
553
554 #[test]
555 fn test_read_move_from_nested_in_insert() {
556 let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
558 <w:p>
559 <w:ins w:id="0" w:author="unknown" w:date="2024-01-01T00:00:00Z">
560 <w:moveFrom w:id="1" w:author="unknown" w:date="2024-01-01T00:00:00Z">
561 <w:r>
562 <w:rPr></w:rPr>
563 <w:t>ghost</w:t>
564 </w:r>
565 </w:moveFrom>
566 <w:r>
567 <w:rPr></w:rPr>
568 <w:t>live</w:t>
569 </w:r>
570 </w:ins>
571 </w:p>
572</w:document>"#;
573 let mut parser = EventReader::new(c.as_bytes());
574 let p = Paragraph::read(&mut parser, &[]).unwrap();
575 assert_eq!(p.raw_text(), "live");
577 }
578
579 #[test]
580 fn test_read_move_from_nested_in_move_to() {
581 let c = r#"<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
583 <w:p>
584 <w:moveTo w:id="2" w:author="unknown" w:date="2024-01-01T00:00:00Z">
585 <w:moveFrom w:id="1" w:author="unknown" w:date="2024-01-01T00:00:00Z">
586 <w:r>
587 <w:rPr></w:rPr>
588 <w:t>ghost</w:t>
589 </w:r>
590 </w:moveFrom>
591 <w:r>
592 <w:rPr></w:rPr>
593 <w:t>live</w:t>
594 </w:r>
595 </w:moveTo>
596 </w:p>
597</w:document>"#;
598 let mut parser = EventReader::new(c.as_bytes());
599 let p = Paragraph::read(&mut parser, &[]).unwrap();
600 assert_eq!(p.raw_text(), "live");
602 }
603}