1use serde::ser::{SerializeStruct, Serializer};
2use serde::Serialize;
3use std::io::Write;
4
5use super::*;
6use crate::documents::BuildXML;
7use crate::types::*;
8use crate::xml_builder::*;
9
10#[derive(Serialize, Debug, Clone, PartialEq)]
11#[serde(rename_all = "camelCase")]
12pub struct Paragraph {
13 pub id: String,
14 pub children: Vec<ParagraphChild>,
15 pub property: ParagraphProperty,
16 pub has_numbering: bool,
17}
18
19impl Default for Paragraph {
20 fn default() -> Self {
21 Self {
22 id: crate::generate_para_id(),
23 children: Vec::new(),
24 property: ParagraphProperty::new(),
25 has_numbering: false,
26 }
27 }
28}
29
30#[derive(Debug, Clone, PartialEq)]
31pub enum ParagraphChild {
32 Run(Box<Run>),
33 Insert(Insert),
34 Delete(Delete),
35 MoveFrom(MoveFrom),
36 MoveTo(MoveTo),
37 BookmarkStart(BookmarkStart),
38 Hyperlink(Hyperlink),
39 BookmarkEnd(BookmarkEnd),
40 CommentStart(Box<CommentRangeStart>),
41 CommentEnd(CommentRangeEnd),
42 StructuredDataTag(Box<StructuredDataTag>),
43 PageNum(Box<PageNum>),
44 NumPages(Box<NumPages>),
45}
46
47impl BuildXML for ParagraphChild {
48 fn build_to<W: Write>(
49 &self,
50 stream: crate::xml::writer::EventWriter<W>,
51 ) -> crate::xml::writer::Result<crate::xml::writer::EventWriter<W>> {
52 match self {
53 ParagraphChild::Run(v) => v.build_to(stream),
54 ParagraphChild::Insert(v) => v.build_to(stream),
55 ParagraphChild::Delete(v) => v.build_to(stream),
56 ParagraphChild::MoveFrom(v) => v.build_to(stream),
57 ParagraphChild::MoveTo(v) => v.build_to(stream),
58 ParagraphChild::Hyperlink(v) => v.build_to(stream),
59 ParagraphChild::BookmarkStart(v) => v.build_to(stream),
60 ParagraphChild::BookmarkEnd(v) => v.build_to(stream),
61 ParagraphChild::CommentStart(v) => v.build_to(stream),
62 ParagraphChild::CommentEnd(v) => v.build_to(stream),
63 ParagraphChild::StructuredDataTag(v) => v.build_to(stream),
64 ParagraphChild::PageNum(v) => v.build_to(stream),
65 ParagraphChild::NumPages(v) => v.build_to(stream),
66 }
67 }
68}
69
70impl Serialize for ParagraphChild {
71 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
72 where
73 S: Serializer,
74 {
75 match *self {
76 ParagraphChild::Run(ref r) => {
77 let mut t = serializer.serialize_struct("Run", 2)?;
78 t.serialize_field("type", "run")?;
79 t.serialize_field("data", r)?;
80 t.end()
81 }
82 ParagraphChild::Insert(ref r) => {
83 let mut t = serializer.serialize_struct("Insert", 2)?;
84 t.serialize_field("type", "insert")?;
85 t.serialize_field("data", r)?;
86 t.end()
87 }
88 ParagraphChild::Delete(ref r) => {
89 let mut t = serializer.serialize_struct("Delete", 2)?;
90 t.serialize_field("type", "delete")?;
91 t.serialize_field("data", r)?;
92 t.end()
93 }
94 ParagraphChild::MoveFrom(ref r) => {
95 let mut t = serializer.serialize_struct("MoveFrom", 2)?;
96 t.serialize_field("type", "moveFrom")?;
97 t.serialize_field("data", r)?;
98 t.end()
99 }
100 ParagraphChild::MoveTo(ref r) => {
101 let mut t = serializer.serialize_struct("MoveTo", 2)?;
102 t.serialize_field("type", "moveTo")?;
103 t.serialize_field("data", r)?;
104 t.end()
105 }
106 ParagraphChild::Hyperlink(ref r) => {
107 let mut t = serializer.serialize_struct("hyperlink", 2)?;
108 t.serialize_field("type", "hyperlink")?;
109 t.serialize_field("data", r)?;
110 t.end()
111 }
112 ParagraphChild::BookmarkStart(ref r) => {
113 let mut t = serializer.serialize_struct("BookmarkStart", 2)?;
114 t.serialize_field("type", "bookmarkStart")?;
115 t.serialize_field("data", r)?;
116 t.end()
117 }
118 ParagraphChild::BookmarkEnd(ref r) => {
119 let mut t = serializer.serialize_struct("BookmarkEnd", 2)?;
120 t.serialize_field("type", "bookmarkEnd")?;
121 t.serialize_field("data", r)?;
122 t.end()
123 }
124 ParagraphChild::CommentStart(ref r) => {
125 let mut t = serializer.serialize_struct("CommentRangeStart", 2)?;
126 t.serialize_field("type", "commentRangeStart")?;
127 t.serialize_field("data", r)?;
128 t.end()
129 }
130 ParagraphChild::CommentEnd(ref r) => {
131 let mut t = serializer.serialize_struct("CommentRangeEnd", 2)?;
132 t.serialize_field("type", "commentRangeEnd")?;
133 t.serialize_field("data", r)?;
134 t.end()
135 }
136 ParagraphChild::StructuredDataTag(ref r) => {
137 let mut t = serializer.serialize_struct("StructuredDataTag", 2)?;
138 t.serialize_field("type", "structuredDataTag")?;
139 t.serialize_field("data", r)?;
140 t.end()
141 }
142 ParagraphChild::PageNum(ref r) => {
143 let mut t = serializer.serialize_struct("PageNum", 2)?;
144 t.serialize_field("type", "pageNum")?;
145 t.serialize_field("data", r)?;
146 t.end()
147 }
148 ParagraphChild::NumPages(ref r) => {
149 let mut t = serializer.serialize_struct("NumPages", 2)?;
150 t.serialize_field("type", "numPages")?;
151 t.serialize_field("data", r)?;
152 t.end()
153 }
154 }
155 }
156}
157
158impl Paragraph {
159 pub fn new() -> Paragraph {
160 Default::default()
161 }
162
163 pub fn id(mut self, id: impl Into<String>) -> Self {
164 self.id = id.into();
165 self
166 }
167
168 pub fn children(&self) -> &Vec<ParagraphChild> {
169 &self.children
170 }
171
172 pub fn add_run(mut self, run: Run) -> Paragraph {
173 self.children.push(ParagraphChild::Run(Box::new(run)));
174 self
175 }
176
177 pub(crate) fn unshift_run(mut self, run: Run) -> Paragraph {
178 self.children.insert(0, ParagraphChild::Run(Box::new(run)));
179 self
180 }
181
182 pub(crate) fn wrap_by_bookmark(mut self, id: usize, name: impl Into<String>) -> Paragraph {
183 self.children.insert(
184 0,
185 ParagraphChild::BookmarkStart(BookmarkStart::new(id, name)),
186 );
187 self.children
188 .push(ParagraphChild::BookmarkEnd(BookmarkEnd::new(id)));
189
190 self
191 }
192
193 pub fn add_hyperlink(mut self, link: Hyperlink) -> Self {
194 self.children.push(ParagraphChild::Hyperlink(link));
195 self
196 }
197
198 pub fn add_structured_data_tag(mut self, t: StructuredDataTag) -> Self {
199 self.children
200 .push(ParagraphChild::StructuredDataTag(Box::new(t)));
201 self
202 }
203
204 pub fn add_insert(mut self, insert: Insert) -> Paragraph {
205 self.children.push(ParagraphChild::Insert(insert));
206 self
207 }
208
209 pub fn add_delete(mut self, delete: Delete) -> Paragraph {
210 self.children.push(ParagraphChild::Delete(delete));
211 self
212 }
213
214 pub fn add_move_from(mut self, move_from: MoveFrom) -> Paragraph {
215 self.children.push(ParagraphChild::MoveFrom(move_from));
216 self
217 }
218
219 pub fn add_move_to(mut self, move_to: MoveTo) -> Paragraph {
220 self.children.push(ParagraphChild::MoveTo(move_to));
221 self
222 }
223
224 pub fn add_bookmark_start(mut self, id: usize, name: impl Into<String>) -> Paragraph {
225 self.children
226 .push(ParagraphChild::BookmarkStart(BookmarkStart::new(id, name)));
227 self
228 }
229
230 pub fn add_bookmark_end(mut self, id: usize) -> Paragraph {
231 self.children
232 .push(ParagraphChild::BookmarkEnd(BookmarkEnd::new(id)));
233 self
234 }
235
236 pub fn add_comment_start(mut self, comment: Comment) -> Paragraph {
237 self.children.push(ParagraphChild::CommentStart(Box::new(
238 CommentRangeStart::new(comment),
239 )));
240 self
241 }
242
243 pub fn add_comment_end(mut self, id: usize) -> Paragraph {
244 self.children
245 .push(ParagraphChild::CommentEnd(CommentRangeEnd::new(id)));
246 self
247 }
248
249 pub fn align(mut self, alignment_type: AlignmentType) -> Paragraph {
250 self.property = self.property.align(alignment_type);
251 self
252 }
253
254 pub fn style(mut self, style_id: &str) -> Paragraph {
255 self.property = self.property.style(style_id);
256 self
257 }
258
259 pub fn snap_to_grid(mut self, v: bool) -> Self {
260 self.property = self.property.snap_to_grid(v);
261 self
262 }
263
264 pub fn keep_next(mut self, v: bool) -> Self {
265 self.property = self.property.keep_next(v);
266 self
267 }
268
269 pub fn keep_lines(mut self, v: bool) -> Self {
270 self.property = self.property.keep_lines(v);
271 self
272 }
273
274 pub fn outline_lvl(mut self, v: usize) -> Self {
275 self.property = self.property.outline_lvl(v);
276 self
277 }
278
279 pub fn page_break_before(mut self, v: bool) -> Self {
280 self.property = self.property.page_break_before(v);
281 self
282 }
283
284 pub fn widow_control(mut self, v: bool) -> Self {
285 self.property = self.property.widow_control(v);
286 self
287 }
288
289 pub fn section_property(mut self, s: SectionProperty) -> Self {
290 self.property = self.property.section_property(s);
291 self
292 }
293
294 pub fn add_tab(mut self, t: Tab) -> Self {
295 self.property = self.property.add_tab(t);
296 self
297 }
298
299 pub fn tabs(mut self, tabs: &[Tab]) -> Self {
300 for tab in tabs {
301 self.property = self.property.add_tab(tab.clone());
302 }
303 self
304 }
305
306 pub fn indent(
307 mut self,
308 left: Option<i32>,
309 special_indent: Option<SpecialIndentType>,
310 end: Option<i32>,
311 start_chars: Option<i32>,
312 ) -> Paragraph {
313 self.property = self.property.indent(left, special_indent, end, start_chars);
314 self
315 }
316
317 pub fn hanging_chars(mut self, chars: i32) -> Paragraph {
318 self.property = self.property.hanging_chars(chars);
319 self
320 }
321
322 pub fn first_line_chars(mut self, chars: i32) -> Paragraph {
323 self.property = self.property.first_line_chars(chars);
324 self
325 }
326
327 pub fn numbering(mut self, id: NumberingId, level: IndentLevel) -> Self {
328 self.property = self.property.numbering(id, level);
329 self.has_numbering = true;
330 self
331 }
332
333 pub fn size(mut self, size: usize) -> Self {
334 self.property.run_property = self.property.run_property.size(size);
335 self
336 }
337
338 pub fn color(mut self, c: impl Into<String>) -> Self {
339 self.property.run_property = self.property.run_property.color(c);
340 self
341 }
342
343 pub fn bold(mut self) -> Self {
344 self.property.run_property = self.property.run_property.bold();
345 self
346 }
347
348 pub fn italic(mut self) -> Self {
349 self.property.run_property = self.property.run_property.italic();
350 self
351 }
352
353 pub fn fonts(mut self, f: RunFonts) -> Self {
354 self.property.run_property = self.property.run_property.fonts(f);
355 self
356 }
357
358 pub fn run_property(mut self, p: RunProperty) -> Self {
359 self.property.run_property = p;
360 self
361 }
362
363 pub fn line_spacing(mut self, spacing: LineSpacing) -> Self {
364 self.property = self.property.line_spacing(spacing);
365 self
366 }
367
368 pub fn character_spacing(mut self, spacing: i32) -> Self {
369 self.property = self.property.character_spacing(spacing);
370 self
371 }
372
373 pub fn delete(mut self, author: impl Into<String>, date: impl Into<String>) -> Self {
374 self.property.run_property.del = Some(Delete::new().author(author).date(date));
375 self
376 }
377
378 pub fn insert(mut self, author: impl Into<String>, date: impl Into<String>) -> Self {
379 self.property.run_property.ins = Some(Insert::new_with_empty().author(author).date(date));
380 self
381 }
382
383 pub fn paragraph_property_change(mut self, p: ParagraphPropertyChange) -> Self {
384 self.property = self.property.paragraph_property_change(p);
385 self
386 }
387
388 pub(crate) fn paragraph_property(mut self, p: ParagraphProperty) -> Self {
390 self.property = p;
391 self
392 }
393
394 pub fn raw_text(&self) -> String {
395 let mut s = "".to_string();
396 for c in self.children.iter() {
399 match c {
400 ParagraphChild::Insert(i) => {
401 for c in i.children.iter() {
402 if let InsertChild::Run(r) = c {
403 for c in r.children.iter() {
404 match c {
405 RunChild::Text(t) => {
406 s.push_str(&t.text);
407 }
408 RunChild::Tab(_t) => {
409 s.push('\t');
410 }
411 RunChild::PTab(_t) => {
412 s.push('\t');
413 }
414 RunChild::Break(_b) => {
415 s.push('\n');
416 }
417 RunChild::CarriageReturn(_cr) => {
418 s.push('\n');
419 }
420 _ => {}
421 }
422 }
423 }
424 }
425 }
426 ParagraphChild::Run(run) => {
427 for c in run.children.iter() {
428 match c {
429 RunChild::Text(t) => {
430 s.push_str(&t.text);
431 }
432 RunChild::Tab(_t) => {
433 s.push('\t');
434 }
435 RunChild::PTab(_t) => {
436 s.push('\t');
437 }
438 RunChild::Break(_b) => {
439 s.push('\n');
440 }
441 RunChild::CarriageReturn(_cr) => {
442 s.push('\n');
443 }
444 _ => {}
445 }
446 }
447 }
448 ParagraphChild::MoveTo(mt) => {
449 for c in mt.children.iter() {
450 if let MoveToChild::Run(r) = c {
451 for c in r.children.iter() {
452 if let RunChild::Text(t) = c {
453 s.push_str(&t.text);
454 }
455 }
456 }
457 }
458 }
459 ParagraphChild::MoveFrom(_) => {
460 }
462 _ => {}
463 }
464 }
465 s
466 }
467
468 pub fn add_page_num(mut self, p: PageNum) -> Self {
469 self.children.push(ParagraphChild::PageNum(Box::new(p)));
470 self
471 }
472
473 pub fn add_num_pages(mut self, p: NumPages) -> Self {
474 self.children.push(ParagraphChild::NumPages(Box::new(p)));
475 self
476 }
477
478 pub fn wrap(mut self, wrap: impl Into<String>) -> Self {
480 self.property.frame_property = Some(FrameProperty {
481 wrap: Some(wrap.into()),
482 ..self.property.frame_property.unwrap_or_default()
483 });
484 self
485 }
486
487 pub fn v_anchor(mut self, anchor: impl Into<String>) -> Self {
488 self.property.frame_property = Some(FrameProperty {
489 v_anchor: Some(anchor.into()),
490 ..self.property.frame_property.unwrap_or_default()
491 });
492 self
493 }
494
495 pub fn h_anchor(mut self, anchor: impl Into<String>) -> Self {
496 self.property.frame_property = Some(FrameProperty {
497 h_anchor: Some(anchor.into()),
498 ..self.property.frame_property.unwrap_or_default()
499 });
500 self
501 }
502
503 pub fn h_rule(mut self, r: impl Into<String>) -> Self {
504 self.property.frame_property = Some(FrameProperty {
505 h_rule: Some(r.into()),
506 ..self.property.frame_property.unwrap_or_default()
507 });
508 self
509 }
510
511 pub fn x_align(mut self, align: impl Into<String>) -> Self {
512 self.property.frame_property = Some(FrameProperty {
513 x_align: Some(align.into()),
514 ..self.property.frame_property.unwrap_or_default()
515 });
516 self
517 }
518
519 pub fn y_align(mut self, align: impl Into<String>) -> Self {
520 self.property.frame_property = Some(FrameProperty {
521 y_align: Some(align.into()),
522 ..self.property.frame_property.unwrap_or_default()
523 });
524 self
525 }
526
527 pub fn h_space(mut self, x: i32) -> Self {
528 self.property.frame_property = Some(FrameProperty {
529 h_space: Some(x),
530 ..self.property.frame_property.unwrap_or_default()
531 });
532 self
533 }
534
535 pub fn v_space(mut self, x: i32) -> Self {
536 self.property.frame_property = Some(FrameProperty {
537 v_space: Some(x),
538 ..self.property.frame_property.unwrap_or_default()
539 });
540 self
541 }
542
543 pub fn frame_x(mut self, x: i32) -> Self {
544 self.property.frame_property = Some(FrameProperty {
545 x: Some(x),
546 ..self.property.frame_property.unwrap_or_default()
547 });
548 self
549 }
550
551 pub fn frame_y(mut self, y: i32) -> Self {
552 self.property.frame_property = Some(FrameProperty {
553 y: Some(y),
554 ..self.property.frame_property.unwrap_or_default()
555 });
556 self
557 }
558
559 pub fn frame_width(mut self, n: u32) -> Self {
560 self.property.frame_property = Some(FrameProperty {
561 w: Some(n),
562 ..self.property.frame_property.unwrap_or_default()
563 });
564 self
565 }
566
567 pub fn frame_height(mut self, n: u32) -> Self {
568 self.property.frame_property = Some(FrameProperty {
569 h: Some(n),
570 ..self.property.frame_property.unwrap_or_default()
571 });
572 self
573 }
574}
575
576impl BuildXML for Paragraph {
577 fn build_to<W: Write>(
578 &self,
579 stream: crate::xml::writer::EventWriter<W>,
580 ) -> crate::xml::writer::Result<crate::xml::writer::EventWriter<W>> {
581 XMLBuilder::from(stream)
582 .open_paragraph(&self.id)?
583 .add_child(&self.property)?
584 .add_children(&self.children)?
585 .close()?
586 .into_inner()
587 }
588}
589
590#[cfg(test)]
591mod tests {
592
593 use super::*;
594 #[cfg(test)]
595 use pretty_assertions::assert_eq;
596 use std::str;
597
598 #[test]
599 fn test_paragraph() {
600 let b = Paragraph::new()
601 .add_run(Run::new().add_text("Hello"))
602 .build();
603 assert_eq!(
604 str::from_utf8(&b).unwrap(),
605 r#"<w:p w14:paraId="12345678"><w:pPr><w:rPr /></w:pPr><w:r><w:rPr /><w:t xml:space="preserve">Hello</w:t></w:r></w:p>"#
606 );
607 }
608
609 #[test]
610 fn test_bookmark() {
611 let b = Paragraph::new()
612 .add_bookmark_start(0, "article")
613 .add_run(Run::new().add_text("Hello"))
614 .add_bookmark_end(0)
615 .build();
616 assert_eq!(
617 str::from_utf8(&b).unwrap(),
618 r#"<w:p w14:paraId="12345678"><w:pPr><w:rPr /></w:pPr><w:bookmarkStart w:id="0" w:name="article" /><w:r><w:rPr /><w:t xml:space="preserve">Hello</w:t></w:r><w:bookmarkEnd w:id="0" /></w:p>"#
619 );
620 }
621
622 #[test]
623 fn test_comment() {
624 let b = Paragraph::new()
625 .add_comment_start(Comment::new(1))
626 .add_run(Run::new().add_text("Hello"))
627 .add_comment_end(1)
628 .build();
629 assert_eq!(
630 str::from_utf8(&b).unwrap(),
631 r#"<w:p w14:paraId="12345678"><w:pPr><w:rPr /></w:pPr><w:commentRangeStart w:id="1" /><w:r><w:rPr /><w:t xml:space="preserve">Hello</w:t></w:r><w:r><w:rPr /></w:r><w:commentRangeEnd w:id="1" /><w:r><w:commentReference w:id="1" /></w:r></w:p>"#
632 );
633 }
634
635 #[test]
636 fn test_numbering() {
637 let b = Paragraph::new()
638 .add_run(Run::new().add_text("Hello"))
639 .numbering(NumberingId::new(0), IndentLevel::new(1))
640 .build();
641 assert_eq!(
642 str::from_utf8(&b).unwrap(),
643 r#"<w:p w14:paraId="12345678"><w:pPr><w:rPr /><w:numPr><w:numId w:val="0" /><w:ilvl w:val="1" /></w:numPr></w:pPr><w:r><w:rPr /><w:t xml:space="preserve">Hello</w:t></w:r></w:p>"#
644 );
645 }
646
647 #[test]
648 fn test_line_spacing_and_character_spacing() {
649 let spacing = LineSpacing::new()
650 .line_rule(LineSpacingType::Auto)
651 .before(20)
652 .after(30)
653 .line(200);
654 let b = Paragraph::new()
655 .line_spacing(spacing)
656 .add_run(Run::new().add_text("Hello"))
657 .build();
658 assert_eq!(
659 str::from_utf8(&b).unwrap(),
660 r#"<w:p w14:paraId="12345678"><w:pPr><w:rPr /><w:spacing w:before="20" w:after="30" w:line="200" w:lineRule="auto" /></w:pPr><w:r><w:rPr /><w:t xml:space="preserve">Hello</w:t></w:r></w:p>"#
661 );
662 }
663
664 #[test]
665 fn test_paragraph_run_json() {
666 let run = Run::new().add_text("Hello");
667 let p = Paragraph::new().add_run(run);
668 assert_eq!(
669 serde_json::to_string(&p).unwrap(),
670 r#"{"id":"12345678","children":[{"type":"run","data":{"runProperty":{},"children":[{"type":"text","data":{"preserveSpace":true,"text":"Hello"}}]}}],"property":{"runProperty":{},"tabs":[]},"hasNumbering":false}"#,
671 );
672 }
673
674 #[test]
675 fn test_paragraph_insert_json() {
676 let run = Run::new().add_text("Hello");
677 let ins = Insert::new(run);
678 let p = Paragraph::new().add_insert(ins);
679 assert_eq!(
680 serde_json::to_string(&p).unwrap(),
681 r#"{"id":"12345678","children":[{"type":"insert","data":{"children":[{"type":"run","data":{"runProperty":{},"children":[{"type":"text","data":{"preserveSpace":true,"text":"Hello"}}]}}],"author":"unnamed","date":"1970-01-01T00:00:00Z"}}],"property":{"runProperty":{},"tabs":[]},"hasNumbering":false}"#
682 );
683 }
684
685 #[test]
686 fn test_raw_text() {
687 let b = Paragraph::new()
688 .add_run(Run::new().add_text("Hello"))
689 .add_insert(Insert::new(Run::new().add_text("World")))
690 .add_delete(Delete::new().add_run(Run::new().add_delete_text("!!!!!")))
691 .raw_text();
692 assert_eq!(b, "HelloWorld".to_owned());
693 }
694
695 #[test]
696 fn test_raw_text_move_from_to_dedup() {
697 let b = Paragraph::new()
700 .add_run(Run::new().add_text("Hello "))
701 .add_move_from(MoveFrom::new().add_run(Run::new().add_text("world")))
702 .add_move_to(MoveTo::new(Run::new().add_text("world")))
703 .raw_text();
704 assert_eq!(b, "Hello world".to_owned());
705 }
706
707 #[test]
708 fn test_raw_text_mixed_content_with_move() {
709 let b = Paragraph::new()
710 .add_run(Run::new().add_text("Start "))
711 .add_move_from(MoveFrom::new().add_run(Run::new().add_text("moved")))
712 .add_move_to(MoveTo::new(Run::new().add_text("moved")))
713 .add_run(Run::new().add_text(" end"))
714 .raw_text();
715 assert_eq!(b, "Start moved end".to_owned());
716 }
717}