docx_rs/documents/elements/
paragraph.rs

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