docx_rs/documents/elements/
section.rs1use super::*;
2use crate::documents::BuildXML;
4use crate::types::*;
5use crate::xml_builder::*;
6use crate::Footer;
7use crate::{delegate_to_field, Header};
8use serde::ser::{SerializeStruct, Serializer};
9use serde::Serialize;
10use std::io::Write;
11
12#[derive(Debug, Clone, PartialEq)]
13pub enum SectionChild {
14 Paragraph(Box<Paragraph>),
15 Table(Box<Table>),
16 BookmarkStart(BookmarkStart),
17 BookmarkEnd(BookmarkEnd),
18 CommentStart(Box<CommentRangeStart>),
19 CommentEnd(CommentRangeEnd),
20 StructuredDataTag(Box<StructuredDataTag>),
21 TableOfContents(Box<TableOfContents>),
22}
23
24impl Serialize for SectionChild {
25 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
26 where
27 S: Serializer,
28 {
29 match *self {
30 SectionChild::Paragraph(ref p) => {
31 let mut t = serializer.serialize_struct("Paragraph", 2)?;
32 t.serialize_field("type", "paragraph")?;
33 t.serialize_field("data", p)?;
34 t.end()
35 }
36 SectionChild::Table(ref c) => {
37 let mut t = serializer.serialize_struct("Table", 2)?;
38 t.serialize_field("type", "table")?;
39 t.serialize_field("data", c)?;
40 t.end()
41 }
42 SectionChild::BookmarkStart(ref c) => {
43 let mut t = serializer.serialize_struct("BookmarkStart", 2)?;
44 t.serialize_field("type", "bookmarkStart")?;
45 t.serialize_field("data", c)?;
46 t.end()
47 }
48 SectionChild::BookmarkEnd(ref c) => {
49 let mut t = serializer.serialize_struct("BookmarkEnd", 2)?;
50 t.serialize_field("type", "bookmarkEnd")?;
51 t.serialize_field("data", c)?;
52 t.end()
53 }
54 SectionChild::CommentStart(ref r) => {
55 let mut t = serializer.serialize_struct("CommentRangeStart", 2)?;
56 t.serialize_field("type", "commentRangeStart")?;
57 t.serialize_field("data", r)?;
58 t.end()
59 }
60 SectionChild::CommentEnd(ref r) => {
61 let mut t = serializer.serialize_struct("CommentRangeEnd", 2)?;
62 t.serialize_field("type", "commentRangeEnd")?;
63 t.serialize_field("data", r)?;
64 t.end()
65 }
66 SectionChild::StructuredDataTag(ref r) => {
67 let mut t = serializer.serialize_struct("StructuredDataTag", 2)?;
68 t.serialize_field("type", "structuredDataTag")?;
69 t.serialize_field("data", r)?;
70 t.end()
71 }
72 SectionChild::TableOfContents(ref r) => {
73 let mut t = serializer.serialize_struct("TableOfContents", 2)?;
74 t.serialize_field("type", "tableOfContents")?;
75 t.serialize_field("data", r)?;
76 t.end()
77 }
78 }
79 }
80}
81
82impl BuildXML for SectionChild {
83 fn build_to<W: Write>(
84 &self,
85 stream: xml::writer::EventWriter<W>,
86 ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
87 match self {
88 SectionChild::Paragraph(v) => v.build_to(stream),
89 SectionChild::Table(v) => v.build_to(stream),
90 SectionChild::BookmarkStart(v) => v.build_to(stream),
91 SectionChild::BookmarkEnd(v) => v.build_to(stream),
92 SectionChild::CommentStart(v) => v.build_to(stream),
93 SectionChild::CommentEnd(v) => v.build_to(stream),
94 SectionChild::StructuredDataTag(v) => v.build_to(stream),
95 SectionChild::TableOfContents(v) => v.build_to(stream),
96 }
97 }
98}
99
100#[derive(Debug, Clone, PartialEq, Serialize)]
101#[serde(rename_all = "camelCase")]
102pub struct Section {
103 pub(crate) property: SectionProperty,
104 pub(crate) children: Vec<SectionChild>,
105 pub(crate) has_numbering: bool,
106 pub(crate) temp_header: Option<Header>,
107 pub(crate) temp_first_header: Option<Header>,
108 pub(crate) temp_even_header: Option<Header>,
109 pub(crate) temp_footer: Option<Footer>,
110 pub(crate) temp_first_footer: Option<Footer>,
111 pub(crate) temp_even_footer: Option<Footer>,
112}
113
114impl Section {
115 pub fn new() -> Section {
116 Default::default()
117 }
118
119 delegate_to_field! {
120 property =>
121 page_size(size: PageSize) -> Self,
122 page_margin(margin: PageMargin) -> Self,
123 page_orient(o: PageOrientationType) -> Self,
124 doc_grid(doc_grid: DocGrid) -> Self,
125 text_direction(direction: String) -> Self,
126 title_pg() -> Self,
127 page_num_type(h: PageNumType) -> Self,
136 }
137
138 pub fn add_paragraph(mut self, p: Paragraph) -> Self {
139 if p.has_numbering {
140 self.has_numbering = true
141 }
142 self.children.push(SectionChild::Paragraph(Box::new(p)));
143 self
144 }
145
146 pub fn add_table(mut self, t: Table) -> Self {
147 if t.has_numbering {
148 self.has_numbering = true
149 }
150 self.children.push(SectionChild::Table(Box::new(t)));
151 self
152 }
153
154 pub fn add_bookmark_start(mut self, id: usize, name: impl Into<String>) -> Self {
155 self.children
156 .push(SectionChild::BookmarkStart(BookmarkStart::new(id, name)));
157 self
158 }
159
160 pub fn add_bookmark_end(mut self, id: usize) -> Self {
161 self.children
162 .push(SectionChild::BookmarkEnd(BookmarkEnd::new(id)));
163 self
164 }
165
166 pub fn add_comment_start(mut self, comment: Comment) -> Self {
167 self.children.push(SectionChild::CommentStart(Box::new(
168 CommentRangeStart::new(comment),
169 )));
170 self
171 }
172
173 pub fn add_comment_end(mut self, id: usize) -> Self {
174 self.children
175 .push(SectionChild::CommentEnd(CommentRangeEnd::new(id)));
176 self
177 }
178
179 pub fn header(mut self, header: Header) -> Self {
180 self.temp_header = Some(header);
181 self
182 }
183
184 pub fn first_header(mut self, header: Header) -> Self {
185 self.temp_first_header = Some(header);
186 self
187 }
188
189 pub fn even_header(mut self, header: Header) -> Self {
190 self.temp_even_header = Some(header);
191 self
192 }
193
194 pub fn footer(mut self, footer: Footer) -> Self {
195 self.temp_footer = Some(footer);
196 self
197 }
198
199 pub fn first_footer(mut self, footer: Footer) -> Self {
200 self.temp_first_footer = Some(footer);
201 self
202 }
203
204 pub fn even_footer(mut self, footer: Footer) -> Self {
205 self.temp_even_footer = Some(footer);
206 self
207 }
208}
209
210impl Default for Section {
211 fn default() -> Self {
212 Self {
213 property: SectionProperty::new(),
214 children: vec![],
215 has_numbering: false,
216 temp_header: None,
217 temp_first_header: None,
218 temp_even_header: None,
219 temp_footer: None,
220 temp_first_footer: None,
221 temp_even_footer: None,
222 }
223 }
224}
225
226impl BuildXML for Section {
227 fn build_to<W: Write>(
228 &self,
229 stream: xml::writer::EventWriter<W>,
230 ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
231 let id = crate::generate_para_id();
232 XMLBuilder::from(stream)
233 .add_children(&self.children)?
234 .open_paragraph(&id)?
235 .open_paragraph_property()?
236 .add_child(&self.property)?
237 .close()?
238 .close()?
239 .into_inner()
240 }
241}
242
243#[cfg(test)]
244mod tests {
245
246 use super::*;
247 #[cfg(test)]
248 use pretty_assertions::assert_eq;
249 use std::str;
250
251 #[test]
252 fn test_section_property_default() {
253 let c = Section::new();
254 let b = c.build();
255 assert_eq!(
256 str::from_utf8(&b).unwrap(),
257 r#"<w:p w14:paraId="12345678"><w:pPr><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:pPr></w:p>"#
258 );
259 }
260
261 #[test]
262 fn test_section_with_paragraph() {
263 let c =
264 Section::new().add_paragraph(Paragraph::new().add_run(Run::new().add_text("Hello")));
265 let b = c.build();
266 assert_eq!(
267 str::from_utf8(&b).unwrap(),
268 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><w:p w14:paraId="12345678"><w:pPr><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:pPr></w:p>"#
269 );
270 }
271}