docx_rs/documents/
document.rs

1use serde::ser::{SerializeStruct, Serializer};
2use serde::Serialize;
3use std::io::Write;
4
5use super::*;
6use crate::documents::BuildXML;
7use crate::xml_builder::*;
8
9#[derive(Debug, Clone, PartialEq, Serialize)]
10#[serde(rename_all = "camelCase")]
11pub struct Document {
12    pub children: Vec<DocumentChild>,
13    pub section_property: SectionProperty,
14    pub has_numbering: bool,
15}
16
17#[derive(Debug, Clone, PartialEq)]
18pub enum DocumentChild {
19    Paragraph(Box<Paragraph>),
20    Table(Box<Table>),
21    BookmarkStart(BookmarkStart),
22    BookmarkEnd(BookmarkEnd),
23    CommentStart(Box<CommentRangeStart>),
24    CommentEnd(CommentRangeEnd),
25    StructuredDataTag(Box<StructuredDataTag>),
26    TableOfContents(Box<TableOfContents>),
27}
28
29impl Serialize for DocumentChild {
30    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
31    where
32        S: Serializer,
33    {
34        match *self {
35            DocumentChild::Paragraph(ref p) => {
36                let mut t = serializer.serialize_struct("Paragraph", 2)?;
37                t.serialize_field("type", "paragraph")?;
38                t.serialize_field("data", p)?;
39                t.end()
40            }
41            DocumentChild::Table(ref c) => {
42                let mut t = serializer.serialize_struct("Table", 2)?;
43                t.serialize_field("type", "table")?;
44                t.serialize_field("data", c)?;
45                t.end()
46            }
47            DocumentChild::BookmarkStart(ref c) => {
48                let mut t = serializer.serialize_struct("BookmarkStart", 2)?;
49                t.serialize_field("type", "bookmarkStart")?;
50                t.serialize_field("data", c)?;
51                t.end()
52            }
53            DocumentChild::BookmarkEnd(ref c) => {
54                let mut t = serializer.serialize_struct("BookmarkEnd", 2)?;
55                t.serialize_field("type", "bookmarkEnd")?;
56                t.serialize_field("data", c)?;
57                t.end()
58            }
59            DocumentChild::CommentStart(ref r) => {
60                let mut t = serializer.serialize_struct("CommentRangeStart", 2)?;
61                t.serialize_field("type", "commentRangeStart")?;
62                t.serialize_field("data", r)?;
63                t.end()
64            }
65            DocumentChild::CommentEnd(ref r) => {
66                let mut t = serializer.serialize_struct("CommentRangeEnd", 2)?;
67                t.serialize_field("type", "commentRangeEnd")?;
68                t.serialize_field("data", r)?;
69                t.end()
70            }
71            DocumentChild::StructuredDataTag(ref r) => {
72                let mut t = serializer.serialize_struct("StructuredDataTag", 2)?;
73                t.serialize_field("type", "structuredDataTag")?;
74                t.serialize_field("data", r)?;
75                t.end()
76            }
77            DocumentChild::TableOfContents(ref r) => {
78                let mut t = serializer.serialize_struct("TableOfContents", 2)?;
79                t.serialize_field("type", "tableOfContents")?;
80                t.serialize_field("data", r)?;
81                t.end()
82            }
83        }
84    }
85}
86
87impl Default for Document {
88    fn default() -> Self {
89        Self {
90            children: Vec::new(),
91            section_property: SectionProperty::new(),
92            has_numbering: false,
93        }
94    }
95}
96
97impl Document {
98    pub fn new() -> Document {
99        Default::default()
100    }
101
102    pub fn add_paragraph(mut self, p: Paragraph) -> Self {
103        if p.has_numbering {
104            self.has_numbering = true
105        }
106        self.children.push(DocumentChild::Paragraph(Box::new(p)));
107        self
108    }
109
110    pub fn add_table(mut self, t: Table) -> Self {
111        if t.has_numbering {
112            self.has_numbering = true
113        }
114        self.children.push(DocumentChild::Table(Box::new(t)));
115        self
116    }
117
118    pub fn add_bookmark_start(mut self, id: usize, name: impl Into<String>) -> Self {
119        self.children
120            .push(DocumentChild::BookmarkStart(BookmarkStart::new(id, name)));
121        self
122    }
123
124    pub fn add_bookmark_end(mut self, id: usize) -> Self {
125        self.children
126            .push(DocumentChild::BookmarkEnd(BookmarkEnd::new(id)));
127        self
128    }
129
130    pub fn add_comment_start(mut self, comment: Comment) -> Self {
131        self.children.push(DocumentChild::CommentStart(Box::new(
132            CommentRangeStart::new(comment),
133        )));
134        self
135    }
136
137    pub fn add_comment_end(mut self, id: usize) -> Self {
138        self.children
139            .push(DocumentChild::CommentEnd(CommentRangeEnd::new(id)));
140        self
141    }
142
143    pub fn title_pg(mut self) -> Self {
144        self.section_property = self.section_property.title_pg();
145        self
146    }
147
148    pub fn page_size(mut self, size: PageSize) -> Self {
149        self.section_property = self.section_property.page_size(size);
150        self
151    }
152
153    pub fn page_margin(mut self, margin: crate::types::PageMargin) -> Self {
154        self.section_property = self.section_property.page_margin(margin);
155        self
156    }
157
158    pub fn page_orient(mut self, o: crate::types::PageOrientationType) -> Self {
159        self.section_property = self.section_property.page_orient(o);
160        self
161    }
162
163    pub fn doc_grid(mut self, doc_grid: DocGrid) -> Self {
164        self.section_property = self.section_property.doc_grid(doc_grid);
165        self
166    }
167
168    pub fn default_section_property(mut self, property: SectionProperty) -> Self {
169        self.section_property = property;
170        self
171    }
172
173    pub fn header(mut self, h: Header, rid: &str) -> Self {
174        self.section_property = self.section_property.header(h, rid);
175        self
176    }
177
178    pub fn first_header(mut self, h: Header, rid: &str) -> Self {
179        self.section_property = self.section_property.first_header(h, rid);
180        self
181    }
182
183    pub(crate) fn first_header_without_title_pg(mut self, h: Header, rid: &str) -> Self {
184        self.section_property = self.section_property.first_header_without_title_pg(h, rid);
185        self
186    }
187
188    pub fn even_header(mut self, h: Header, rid: &str) -> Self {
189        self.section_property = self.section_property.even_header(h, rid);
190        self
191    }
192
193    pub fn footer(mut self, h: Footer, rid: &str) -> Self {
194        self.section_property = self.section_property.footer(h, rid);
195        self
196    }
197
198    pub fn first_footer(mut self, h: Footer, rid: &str) -> Self {
199        self.section_property = self.section_property.first_footer(h, rid);
200        self
201    }
202
203    pub(crate) fn first_footer_without_title_pg(mut self, h: Footer, rid: &str) -> Self {
204        self.section_property = self.section_property.first_footer_without_title_pg(h, rid);
205        self
206    }
207
208    pub fn even_footer(mut self, h: Footer, rid: &str) -> Self {
209        self.section_property = self.section_property.even_footer(h, rid);
210        self
211    }
212
213    pub fn add_structured_data_tag(mut self, t: StructuredDataTag) -> Self {
214        if t.has_numbering {
215            self.has_numbering = true
216        }
217        self.children
218            .push(DocumentChild::StructuredDataTag(Box::new(t)));
219        self
220    }
221
222    pub fn add_table_of_contents(mut self, t: TableOfContents) -> Self {
223        self.children
224            .push(DocumentChild::TableOfContents(Box::new(t)));
225        self
226    }
227
228    pub fn columns(mut self, col: usize) -> Self {
229        self.section_property.columns = col;
230        self
231    }
232
233    pub fn text_direction(mut self, direction: String) -> Self {
234        self.section_property.text_direction = direction;
235        self
236    }
237
238    pub fn page_num_type(mut self, p: PageNumType) -> Self {
239        self.section_property = self.section_property.page_num_type(p);
240        self
241    }
242}
243
244impl BuildXML for DocumentChild {
245    fn build_to<W: Write>(
246        &self,
247        stream: xml::writer::EventWriter<W>,
248    ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
249        match self {
250            DocumentChild::Paragraph(v) => v.build_to(stream),
251            DocumentChild::Table(v) => v.build_to(stream),
252            DocumentChild::BookmarkStart(v) => v.build_to(stream),
253            DocumentChild::BookmarkEnd(v) => v.build_to(stream),
254            DocumentChild::CommentStart(v) => v.build_to(stream),
255            DocumentChild::CommentEnd(v) => v.build_to(stream),
256            DocumentChild::StructuredDataTag(v) => v.build_to(stream),
257            DocumentChild::TableOfContents(v) => v.build_to(stream),
258        }
259    }
260}
261
262impl BuildXML for Document {
263    fn build_to<W: Write>(
264        &self,
265        stream: xml::writer::EventWriter<W>,
266    ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
267        XMLBuilder::from(stream)
268            .declaration(Some(true))?
269            .open_document()?
270            .open_body()?
271            .add_children(&self.children)?
272            .add_child(&self.section_property)?
273            .close()?
274            .close()?
275            .into_inner()
276    }
277}
278
279#[cfg(test)]
280mod tests {
281
282    use super::super::Run;
283    use super::*;
284    #[cfg(test)]
285    use pretty_assertions::assert_eq;
286    use std::str;
287
288    #[test]
289    fn test_document() {
290        let b = Document::new()
291            .add_paragraph(Paragraph::new().add_run(Run::new().add_text("Hello")))
292            .build();
293        assert_eq!(
294            str::from_utf8(&b).unwrap(),
295            r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:document xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" mc:Ignorable="w14 wp14"><w:body><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><w:sectPr><w:pgSz w:w="11906" w:h="16838" /><w:pgMar w:top="1985" w:right="1701" w:bottom="1701" w:left="1701" w:header="851" w:footer="992" w:gutter="0" /><w:cols w:space="425" w:num="1" /></w:sectPr></w:body></w:document>"#
296        );
297    }
298
299    #[test]
300    fn test_document_with_toc() {
301        let toc = TableOfContents::new().heading_styles_range(1, 3);
302        let b = Document::new().add_table_of_contents(toc).build();
303        assert_eq!(
304            str::from_utf8(&b).unwrap(),
305            r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:document xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" mc:Ignorable="w14 wp14"><w:body><w:sdt><w:sdtPr><w:rPr /></w:sdtPr><w:sdtContent><w:p w14:paraId="12345678"><w:pPr><w:rPr /></w:pPr><w:r><w:rPr /><w:fldChar w:fldCharType="begin" w:dirty="true" /><w:instrText>TOC \o &quot;1-3&quot;</w:instrText><w:fldChar w:fldCharType="separate" w:dirty="false" /></w:r></w:p><w:p w14:paraId="12345678"><w:pPr><w:rPr /></w:pPr><w:r><w:rPr /><w:fldChar w:fldCharType="end" w:dirty="false" /></w:r></w:p></w:sdtContent></w:sdt><w:sectPr><w:pgSz w:w="11906" w:h="16838" /><w:pgMar w:top="1985" w:right="1701" w:bottom="1701" w:left="1701" w:header="851" w:footer="992" w:gutter="0" /><w:cols w:space="425" w:num="1" /></w:sectPr></w:body></w:document>"#
306        );
307    }
308
309    #[test]
310    fn test_document_cols() {
311        let b = Document::new()
312            .columns(2)
313            .add_paragraph(Paragraph::new().add_run(Run::new().add_text("Hello")))
314            .build();
315        assert_eq!(
316            str::from_utf8(&b).unwrap(),
317            r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:document xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" mc:Ignorable="w14 wp14"><w:body><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><w:sectPr><w:pgSz w:w="11906" w:h="16838" /><w:pgMar w:top="1985" w:right="1701" w:bottom="1701" w:left="1701" w:header="851" w:footer="992" w:gutter="0" /><w:cols w:space="425" w:num="2" /></w:sectPr></w:body></w:document>"#
318        );
319    }
320}