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 "1-3"</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}