linch_docx_rs/document/
body.rs1use crate::document::{Paragraph, Table};
4use crate::error::Result;
5use crate::xml::RawXmlNode;
6use quick_xml::events::{BytesEnd, BytesStart, Event};
7use quick_xml::{Reader, Writer};
8use std::io::BufRead;
9
10#[derive(Clone, Debug)]
12pub enum BlockContent {
13 Paragraph(Paragraph),
15 Table(Table),
17 Unknown(RawXmlNode),
19}
20
21#[derive(Clone, Debug, Default)]
23pub struct Body {
24 pub content: Vec<BlockContent>,
26 pub section_properties: Option<RawXmlNode>,
28}
29
30impl Body {
31 pub fn from_reader<R: BufRead>(reader: &mut Reader<R>) -> Result<Self> {
33 let mut body = Body::default();
34 let mut buf = Vec::new();
35
36 loop {
37 match reader.read_event_into(&mut buf)? {
38 Event::Start(e) => {
39 let name = e.name();
40 let local = name.local_name();
41
42 match local.as_ref() {
43 b"p" => {
44 let para = Paragraph::from_reader(reader, &e)?;
45 body.content.push(BlockContent::Paragraph(para));
46 }
47 b"tbl" => {
48 let table = Table::from_reader(reader, &e)?;
49 body.content.push(BlockContent::Table(table));
50 }
51 b"sectPr" => {
52 let raw = crate::xml::RawXmlElement::from_reader(reader, &e)?;
54 body.section_properties = Some(RawXmlNode::Element(raw));
55 }
56 _ => {
57 let raw = crate::xml::RawXmlElement::from_reader(reader, &e)?;
59 body.content
60 .push(BlockContent::Unknown(RawXmlNode::Element(raw)));
61 }
62 }
63 }
64 Event::Empty(e) => {
65 let name = e.name();
66 let local = name.local_name();
67
68 match local.as_ref() {
69 b"p" => {
70 let para = Paragraph::from_empty(&e)?;
72 body.content.push(BlockContent::Paragraph(para));
73 }
74 _ => {
75 let raw = crate::xml::RawXmlElement {
77 name: String::from_utf8_lossy(e.name().as_ref()).to_string(),
78 attributes: e
79 .attributes()
80 .filter_map(|a| a.ok())
81 .map(|a| {
82 (
83 String::from_utf8_lossy(a.key.as_ref()).to_string(),
84 String::from_utf8_lossy(&a.value).to_string(),
85 )
86 })
87 .collect(),
88 children: Vec::new(),
89 self_closing: true,
90 };
91 body.content
92 .push(BlockContent::Unknown(RawXmlNode::Element(raw)));
93 }
94 }
95 }
96 Event::End(e) => {
97 if e.name().local_name().as_ref() == b"body" {
98 break;
99 }
100 }
101 Event::Eof => break,
102 _ => {}
103 }
104 buf.clear();
105 }
106
107 Ok(body)
108 }
109
110 pub fn paragraphs(&self) -> impl Iterator<Item = &Paragraph> {
112 self.content.iter().filter_map(|c| {
113 if let BlockContent::Paragraph(p) = c {
114 Some(p)
115 } else {
116 None
117 }
118 })
119 }
120
121 pub fn paragraphs_mut(&mut self) -> impl Iterator<Item = &mut Paragraph> {
123 self.content.iter_mut().filter_map(|c| {
124 if let BlockContent::Paragraph(p) = c {
125 Some(p)
126 } else {
127 None
128 }
129 })
130 }
131
132 pub fn tables(&self) -> impl Iterator<Item = &Table> {
134 self.content.iter().filter_map(|c| {
135 if let BlockContent::Table(t) = c {
136 Some(t)
137 } else {
138 None
139 }
140 })
141 }
142
143 pub fn write_to<W: std::io::Write>(&self, writer: &mut Writer<W>) -> Result<()> {
145 writer.write_event(Event::Start(BytesStart::new("w:body")))?;
146
147 for content in &self.content {
149 content.write_to(writer)?;
150 }
151
152 if let Some(sect_pr) = &self.section_properties {
154 sect_pr.write_to(writer)?;
155 }
156
157 writer.write_event(Event::End(BytesEnd::new("w:body")))?;
158 Ok(())
159 }
160
161 pub fn add_paragraph(&mut self, para: Paragraph) {
163 self.content.push(BlockContent::Paragraph(para));
164 }
165
166 pub fn add_table(&mut self, table: Table) {
168 self.content.push(BlockContent::Table(table));
169 }
170}
171
172impl BlockContent {
173 pub fn write_to<W: std::io::Write>(&self, writer: &mut Writer<W>) -> Result<()> {
175 match self {
176 BlockContent::Paragraph(para) => para.write_to(writer),
177 BlockContent::Table(table) => table.write_to(writer),
178 BlockContent::Unknown(node) => node.write_to(writer),
179 }
180 }
181}