Skip to content

Commit 79566d4

Browse files
authored
for now support section (#850)
* impl: section * fix: section interface * fix: doc error
1 parent e308644 commit 79566d4

File tree

6 files changed

+300
-1
lines changed

6 files changed

+300
-1
lines changed

docx-core/examples/section.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
use docx_rs::*;
2+
3+
pub fn main() -> Result<(), DocxError> {
4+
let path = std::path::Path::new("./output/examples/section.docx");
5+
let file = std::fs::File::create(path).unwrap();
6+
let section = Section::new()
7+
.add_paragraph(Paragraph::new().add_run(Run::new().add_text("Hello")))
8+
.page_size(PageSize::new().size(10000, 10000))
9+
.page_orient(PageOrientationType::Landscape);
10+
11+
Docx::new()
12+
.add_section(section)
13+
.add_paragraph(Paragraph::new().add_run(Run::new().add_text("World")))
14+
.build()
15+
.pack(file)?;
16+
Ok(())
17+
}

docx-core/src/documents/document.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ pub enum DocumentChild {
2424
CommentEnd(CommentRangeEnd),
2525
StructuredDataTag(Box<StructuredDataTag>),
2626
TableOfContents(Box<TableOfContents>),
27+
Section(Box<Section>),
2728
}
2829

2930
impl Serialize for DocumentChild {
@@ -80,6 +81,12 @@ impl Serialize for DocumentChild {
8081
t.serialize_field("data", r)?;
8182
t.end()
8283
}
84+
DocumentChild::Section(ref r) => {
85+
let mut t = serializer.serialize_struct("Section", 2)?;
86+
t.serialize_field("type", "section")?;
87+
t.serialize_field("data", r)?;
88+
t.end()
89+
}
8390
}
8491
}
8592
}
@@ -140,6 +147,11 @@ impl Document {
140147
self
141148
}
142149

150+
pub fn add_section(mut self, sec: Section) -> Self {
151+
self.children.push(DocumentChild::Section(Box::new(sec)));
152+
self
153+
}
154+
143155
pub fn title_pg(mut self) -> Self {
144156
self.section_property = self.section_property.title_pg();
145157
self
@@ -255,6 +267,7 @@ impl BuildXML for DocumentChild {
255267
DocumentChild::CommentEnd(v) => v.build_to(stream),
256268
DocumentChild::StructuredDataTag(v) => v.build_to(stream),
257269
DocumentChild::TableOfContents(v) => v.build_to(stream),
270+
DocumentChild::Section(v) => v.build_to(stream),
258271
}
259272
}
260273
}

docx-core/src/documents/elements/section.rs

Lines changed: 180 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,193 @@
11
use super::*;
2+
// use crate::create_header_rid;
23
use crate::documents::BuildXML;
4+
use crate::types::*;
35
use crate::xml_builder::*;
6+
use crate::{delegate_getters_to_field, delegate_to_field, Footer, Header};
7+
use serde::ser::{SerializeStruct, Serializer};
8+
use serde::Serialize;
49
use std::io::Write;
510

6-
use serde::Serialize;
11+
#[derive(Debug, Clone, PartialEq)]
12+
pub enum SectionChild {
13+
Paragraph(Box<Paragraph>),
14+
Table(Box<Table>),
15+
BookmarkStart(BookmarkStart),
16+
BookmarkEnd(BookmarkEnd),
17+
CommentStart(Box<CommentRangeStart>),
18+
CommentEnd(CommentRangeEnd),
19+
StructuredDataTag(Box<StructuredDataTag>),
20+
TableOfContents(Box<TableOfContents>),
21+
}
22+
23+
impl Serialize for SectionChild {
24+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
25+
where
26+
S: Serializer,
27+
{
28+
match *self {
29+
SectionChild::Paragraph(ref p) => {
30+
let mut t = serializer.serialize_struct("Paragraph", 2)?;
31+
t.serialize_field("type", "paragraph")?;
32+
t.serialize_field("data", p)?;
33+
t.end()
34+
}
35+
SectionChild::Table(ref c) => {
36+
let mut t = serializer.serialize_struct("Table", 2)?;
37+
t.serialize_field("type", "table")?;
38+
t.serialize_field("data", c)?;
39+
t.end()
40+
}
41+
SectionChild::BookmarkStart(ref c) => {
42+
let mut t = serializer.serialize_struct("BookmarkStart", 2)?;
43+
t.serialize_field("type", "bookmarkStart")?;
44+
t.serialize_field("data", c)?;
45+
t.end()
46+
}
47+
SectionChild::BookmarkEnd(ref c) => {
48+
let mut t = serializer.serialize_struct("BookmarkEnd", 2)?;
49+
t.serialize_field("type", "bookmarkEnd")?;
50+
t.serialize_field("data", c)?;
51+
t.end()
52+
}
53+
SectionChild::CommentStart(ref r) => {
54+
let mut t = serializer.serialize_struct("CommentRangeStart", 2)?;
55+
t.serialize_field("type", "commentRangeStart")?;
56+
t.serialize_field("data", r)?;
57+
t.end()
58+
}
59+
SectionChild::CommentEnd(ref r) => {
60+
let mut t = serializer.serialize_struct("CommentRangeEnd", 2)?;
61+
t.serialize_field("type", "commentRangeEnd")?;
62+
t.serialize_field("data", r)?;
63+
t.end()
64+
}
65+
SectionChild::StructuredDataTag(ref r) => {
66+
let mut t = serializer.serialize_struct("StructuredDataTag", 2)?;
67+
t.serialize_field("type", "structuredDataTag")?;
68+
t.serialize_field("data", r)?;
69+
t.end()
70+
}
71+
SectionChild::TableOfContents(ref r) => {
72+
let mut t = serializer.serialize_struct("TableOfContents", 2)?;
73+
t.serialize_field("type", "tableOfContents")?;
74+
t.serialize_field("data", r)?;
75+
t.end()
76+
}
77+
}
78+
}
79+
}
80+
81+
impl BuildXML for SectionChild {
82+
fn build_to<W: Write>(
83+
&self,
84+
stream: xml::writer::EventWriter<W>,
85+
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
86+
match self {
87+
SectionChild::Paragraph(v) => v.build_to(stream),
88+
SectionChild::Table(v) => v.build_to(stream),
89+
SectionChild::BookmarkStart(v) => v.build_to(stream),
90+
SectionChild::BookmarkEnd(v) => v.build_to(stream),
91+
SectionChild::CommentStart(v) => v.build_to(stream),
92+
SectionChild::CommentEnd(v) => v.build_to(stream),
93+
SectionChild::StructuredDataTag(v) => v.build_to(stream),
94+
SectionChild::TableOfContents(v) => v.build_to(stream),
95+
}
96+
}
97+
}
798

899
#[derive(Debug, Clone, PartialEq, Serialize)]
9100
#[serde(rename_all = "camelCase")]
10101
pub struct Section {
11102
property: SectionProperty,
103+
children: Vec<SectionChild>,
104+
pub(crate) has_numbering: bool,
12105
}
13106

14107
impl Section {
15108
pub fn new() -> Section {
16109
Default::default()
17110
}
111+
112+
delegate_to_field! {
113+
property =>
114+
page_size(size: PageSize) -> Self,
115+
page_margin(margin: PageMargin) -> Self,
116+
page_orient(o: PageOrientationType) -> Self,
117+
doc_grid(doc_grid: DocGrid) -> Self,
118+
text_direction(direction: String) -> Self,
119+
title_pg() -> Self,
120+
// header(h: Header, rid: &str) -> Self,
121+
// first_header(h: Header, rid: &str) -> Self,
122+
// first_header_without_title_pg(h: Header, rid: &str) -> Self,
123+
// even_header(h: Header, rid: &str) -> Self,
124+
// footer(h: Footer, rid: &str) -> Self,
125+
// first_footer(h: Footer, rid: &str) -> Self,
126+
// first_footer_without_title_pg(h: Footer, rid: &str) -> Self,
127+
// even_footer(h: Footer, rid: &str) -> Self,
128+
page_num_type(h: PageNumType) -> Self,
129+
}
130+
131+
delegate_getters_to_field! {
132+
property =>
133+
get_headers() -> Vec<&Header>,
134+
get_footers() -> Vec<&Footer>,
135+
}
136+
137+
pub fn add_paragraph(mut self, p: Paragraph) -> Self {
138+
if p.has_numbering {
139+
self.has_numbering = true
140+
}
141+
self.children.push(SectionChild::Paragraph(Box::new(p)));
142+
self
143+
}
144+
145+
pub fn add_table(mut self, t: Table) -> Self {
146+
if t.has_numbering {
147+
self.has_numbering = true
148+
}
149+
self.children.push(SectionChild::Table(Box::new(t)));
150+
self
151+
}
152+
153+
pub fn add_bookmark_start(mut self, id: usize, name: impl Into<String>) -> Self {
154+
self.children
155+
.push(SectionChild::BookmarkStart(BookmarkStart::new(id, name)));
156+
self
157+
}
158+
159+
pub fn add_bookmark_end(mut self, id: usize) -> Self {
160+
self.children
161+
.push(SectionChild::BookmarkEnd(BookmarkEnd::new(id)));
162+
self
163+
}
164+
165+
pub fn add_comment_start(mut self, comment: Comment) -> Self {
166+
self.children.push(SectionChild::CommentStart(Box::new(
167+
CommentRangeStart::new(comment),
168+
)));
169+
self
170+
}
171+
172+
pub fn add_comment_end(mut self, id: usize) -> Self {
173+
self.children
174+
.push(SectionChild::CommentEnd(CommentRangeEnd::new(id)));
175+
self
176+
}
177+
178+
// TODO: inject count or create temp header
179+
// pub fn header(mut self, header: Header) -> Self {
180+
// self.property = self.property.header(header, &create_header_rid(count));
181+
// self
182+
// }
18183
}
19184

20185
impl Default for Section {
21186
fn default() -> Self {
22187
Self {
23188
property: SectionProperty::new(),
189+
children: vec![],
190+
has_numbering: false,
24191
}
25192
}
26193
}
@@ -32,6 +199,7 @@ impl BuildXML for Section {
32199
) -> xml::writer::Result<xml::writer::EventWriter<W>> {
33200
let id = crate::generate_para_id();
34201
XMLBuilder::from(stream)
202+
.add_children(&self.children)?
35203
.open_paragraph(&id)?
36204
.open_paragraph_property()?
37205
.add_child(&self.property)?
@@ -58,4 +226,15 @@ mod tests {
58226
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>"#
59227
);
60228
}
229+
230+
#[test]
231+
fn test_section_with_paragraph() {
232+
let c =
233+
Section::new().add_paragraph(Paragraph::new().add_run(Run::new().add_text("Hello")));
234+
let b = c.build();
235+
assert_eq!(
236+
str::from_utf8(&b).unwrap(),
237+
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>"#
238+
);
239+
}
61240
}

docx-core/src/documents/mod.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,25 @@ impl Docx {
297297
self
298298
}
299299

300+
pub fn add_section(mut self, s: Section) -> Docx {
301+
if s.has_numbering {
302+
// If this document has numbering, set numberings.xml to document_rels.
303+
// This is because numberings.xml without numbering cause an error on word online.
304+
self.document_rels.has_numberings = true;
305+
}
306+
let headers = s.get_headers();
307+
for header in headers {
308+
if header.has_numbering {
309+
self.document_rels.has_numberings = true;
310+
}
311+
let count = self.document_rels.header_count + 1;
312+
self.document_rels.header_count = count;
313+
self.content_type = self.content_type.add_header();
314+
}
315+
self.document = self.document.add_section(s);
316+
self
317+
}
318+
300319
pub fn add_structured_data_tag(mut self, t: StructuredDataTag) -> Docx {
301320
if t.has_numbering {
302321
// If this document has numbering, set numberings.xml to document_rels.

docx-core/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ mod documents;
22
#[allow(hidden_glob_reexports)] // should rename?
33
mod errors;
44
mod escape;
5+
mod macros;
56
mod reader;
67
mod types;
78
mod xml_builder;

docx-core/src/macros.rs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/// Macro to delegate methods to a specified field
2+
///
3+
/// This macro generates methods that delegate to methods on a field of the struct.
4+
/// Each delegated method will call the corresponding method on the specified field
5+
/// and return `self` for method chaining.
6+
///
7+
/// # Example
8+
///
9+
/// ```rust,ignore
10+
/// use docx_rs::delegate_to_field;
11+
///
12+
/// struct Wrapper {
13+
/// inner: SomeType,
14+
/// }
15+
///
16+
/// impl Wrapper {
17+
/// delegate_to_field! {
18+
/// inner =>
19+
/// method1(param: i32) -> Self,
20+
/// method2(a: String, b: bool) -> Self,
21+
/// method3() -> Self,
22+
/// }
23+
/// }
24+
/// ```
25+
#[macro_export]
26+
macro_rules! delegate_to_field {
27+
($field:ident => $($method:ident($($param:ident: $param_type:ty),*) -> Self),* $(,)?) => {
28+
$(
29+
pub fn $method(mut self, $($param: $param_type),*) -> Self {
30+
self.$field = self.$field.$method($($param),*);
31+
self
32+
}
33+
)*
34+
};
35+
}
36+
37+
/// Macro to delegate getter methods to a specified field
38+
///
39+
/// This macro generates getter methods that delegate to methods on a field of the struct.
40+
/// Each delegated method will call the corresponding method on the specified field
41+
/// and return the result directly.
42+
///
43+
/// # Example
44+
///
45+
/// ```rust,ignore
46+
/// use docx_rs::delegate_getters_to_field;
47+
///
48+
/// struct Wrapper {
49+
/// inner: SomeType,
50+
/// }
51+
///
52+
/// impl Wrapper {
53+
/// delegate_getters_to_field! {
54+
/// inner =>
55+
/// get_value() -> i32,
56+
/// get_name() -> String,
57+
/// is_active() -> bool,
58+
/// }
59+
/// }
60+
/// ```
61+
#[macro_export]
62+
macro_rules! delegate_getters_to_field {
63+
($field:ident => $($method:ident() -> $return_type:ty),* $(,)?) => {
64+
$(
65+
pub fn $method(&self) -> $return_type {
66+
self.$field.$method()
67+
}
68+
)*
69+
};
70+
}

0 commit comments

Comments
 (0)