docx_reader/documents/
content_types.rs

1use serde::{Deserialize, Serialize};
2use std::collections::BTreeMap;
3use std::io::Read;
4use xml::reader::{EventReader, XmlEvent};
5
6use crate::reader::{FromXML, ReaderError};
7
8#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
9pub struct ContentTypes {
10	types: BTreeMap<String, String>,
11	web_extension_count: usize,
12	custom_xml_count: usize,
13	header_count: usize,
14	footer_count: usize,
15}
16
17impl ContentTypes {
18	pub fn new() -> ContentTypes {
19		Default::default()
20	}
21
22	pub fn add_content(mut self, path: impl Into<String>, namespace: impl Into<String>) -> Self {
23		self.types.insert(path.into(), namespace.into());
24		self
25	}
26
27	pub fn set_default(mut self) -> ContentTypes {
28		self.types.insert(
29			"/_rels/.rels".to_owned(),
30			"application/vnd.openxmlformats-package.relationships+xml".to_owned(),
31		);
32		self.types.insert(
33			"/docProps/app.xml".to_owned(),
34			"application/vnd.openxmlformats-officedocument.extended-properties+xml".to_owned(),
35		);
36		self.types.insert(
37			"/docProps/core.xml".to_owned(),
38			"application/vnd.openxmlformats-package.core-properties+xml".to_owned(),
39		);
40		self.types.insert(
41			"/word/_rels/document.xml.rels".to_owned(),
42			"application/vnd.openxmlformats-package.relationships+xml".to_owned(),
43		);
44		self.types.insert(
45			"/word/settings.xml".to_owned(),
46			"application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml"
47				.to_owned(),
48		);
49		self.types.insert(
50			"/word/fontTable.xml".to_owned(),
51			"application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml"
52				.to_owned(),
53		);
54		self.types.insert(
55			"/word/document.xml".to_owned(),
56			"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"
57				.to_owned(),
58		);
59		self.types.insert(
60			"/word/styles.xml".to_owned(),
61			"application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml".to_owned(),
62		);
63		self.types.insert(
64			"/word/comments.xml".to_owned(),
65			"application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml"
66				.to_owned(),
67		);
68		self.types.insert(
69			"/word/numbering.xml".to_owned(),
70			"application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml"
71				.to_owned(),
72		);
73		self.types.insert(
74			"/word/commentsExtended.xml".to_owned(),
75			"application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml"
76				.to_owned(),
77		);
78		self.types.insert(
79			"/docProps/custom.xml".to_owned(),
80			"application/vnd.openxmlformats-officedocument.custom-properties+xml".to_owned(),
81		);
82		self
83	}
84
85	pub fn add_taskpanes(mut self) -> Self {
86		self.types.insert(
87			"/word/webextensions/taskpanes.xml".to_owned(),
88			"application/vnd.ms-office.webextensiontaskpanes+xml".to_owned(),
89		);
90		self
91	}
92
93	pub fn add_web_extensions(mut self) -> Self {
94		self.types.insert(
95			format!(
96				"/word/webextensions/webextension{}.xml",
97				self.web_extension_count
98			),
99			"application/vnd.ms-office.webextension+xml".to_owned(),
100		);
101		self.web_extension_count += 1;
102		self
103	}
104
105	pub fn add_custom_xml(mut self) -> Self {
106		self.types.insert(
107			format!("/customXml/itemProps{}.xml", self.web_extension_count),
108			"application/vnd.openxmlformats-officedocument.customXmlProperties+xml".to_owned(),
109		);
110		self.custom_xml_count += 1;
111		self
112	}
113
114	pub fn add_header(mut self) -> Self {
115		self.header_count += 1;
116		self.types.insert(
117			format!("/word/header{}.xml", self.header_count),
118			"application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml".to_owned(),
119		);
120		self
121	}
122
123	pub fn add_footer(mut self) -> Self {
124		self.footer_count += 1;
125		self.types.insert(
126			format!("/word/footer{}.xml", self.footer_count),
127			"application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml".to_owned(),
128		);
129		self
130	}
131}
132
133impl Default for ContentTypes {
134	fn default() -> Self {
135		ContentTypes {
136			types: BTreeMap::new(),
137			web_extension_count: 1,
138			custom_xml_count: 1,
139			header_count: 0,
140			footer_count: 0,
141		}
142	}
143}
144
145impl FromXML for ContentTypes {
146	fn from_xml<R: Read>(reader: R) -> Result<Self, ReaderError> {
147		let parser = EventReader::new(reader);
148		let mut s = Self::default();
149		let mut depth = 0;
150		for e in parser {
151			match e {
152				Ok(XmlEvent::StartElement { attributes, .. }) => {
153					if depth == 1 {
154						let namespace = attributes[0].value.clone();
155						let path = attributes[1].value.clone();
156						s = s.add_content(path, namespace);
157					}
158					depth += 1;
159				}
160				Ok(XmlEvent::EndElement { .. }) => {
161					depth -= 1;
162				}
163				Err(_) => {}
164				_ => {}
165			}
166		}
167		Ok(s)
168	}
169}