docx_reader/documents/
mod.rs

1use std::str::FromStr;
2
3mod content_types;
4mod custom_item_property;
5mod custom_item_rels;
6mod doc_props;
7mod document;
8mod document_rels;
9mod elements;
10mod font_table;
11mod footer;
12mod footer_id;
13mod header;
14mod header_id;
15mod hyperlink_id;
16mod numberings;
17mod paragraph_id;
18mod pic_id;
19mod rels;
20mod settings;
21mod styles;
22mod taskpanes;
23mod taskpanes_rels;
24mod theme;
25mod toc_key;
26mod web_settings;
27mod webextension;
28
29pub(crate) use hyperlink_id::*;
30use image::ImageFormat;
31pub(crate) use paragraph_id::*;
32pub(crate) use pic_id::*;
33
34pub use content_types::*;
35pub use custom_item_property::*;
36pub use custom_item_rels::*;
37pub use doc_props::*;
38pub use document::*;
39pub use document_rels::*;
40pub use elements::*;
41pub use font_table::*;
42pub use footer::*;
43pub use footer_id::*;
44pub use header::*;
45pub use header_id::*;
46pub use numberings::*;
47pub use rels::*;
48pub use settings::*;
49pub use styles::*;
50pub use taskpanes::*;
51pub use taskpanes_rels::*;
52pub use theme::*;
53pub use toc_key::*;
54pub use web_settings::*;
55pub use webextension::*;
56
57use serde::{ser, Serialize};
58
59#[derive(Debug, Clone)]
60pub struct Image(pub Vec<u8>);
61
62#[derive(Debug, Clone)]
63pub struct Png(pub Vec<u8>);
64
65pub type ImageIdAndPath = (String, String);
66pub type ImageIdAndBuf = (String, Vec<u8>);
67
68impl ser::Serialize for Image {
69	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
70	where
71		S: ser::Serializer,
72	{
73		let base64 = base64::display::Base64Display::with_config(&*self.0, base64::STANDARD);
74		serializer.collect_str(&base64)
75	}
76}
77
78impl ser::Serialize for Png {
79	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
80	where
81		S: ser::Serializer,
82	{
83		let base64 = base64::display::Base64Display::with_config(&*self.0, base64::STANDARD);
84		serializer.collect_str(&base64)
85	}
86}
87
88#[derive(Debug, Clone, Serialize)]
89#[serde(rename_all = "camelCase")]
90pub struct Docx {
91	pub content_type: ContentTypes,
92	pub rels: Rels,
93	pub document_rels: DocumentRels,
94	pub doc_props: DocProps,
95	pub styles: Styles,
96	pub document: Document,
97	pub numberings: Numberings,
98	pub settings: Settings,
99	pub font_table: FontTable,
100	pub media: Vec<(String, Vec<u8>)>,
101	pub web_settings: WebSettings,
102	pub taskpanes: Option<Taskpanes>,
103	pub taskpanes_rels: TaskpanesRels,
104	pub web_extensions: Vec<WebExtension>,
105	pub custom_item_props: Vec<CustomItemProperty>,
106	pub custom_item_rels: Vec<CustomItemRels>,
107	pub themes: Vec<Theme>,
108	pub images: Vec<(String, String, Image, Png)>,
109	pub hyperlinks: Vec<(String, String, String)>,
110}
111
112impl Default for Docx {
113	fn default() -> Self {
114		let content_type = ContentTypes::new().set_default();
115		let rels = Rels::new().set_default();
116		let doc_props = DocProps::new(CorePropsConfig::new());
117		let styles = Styles::new();
118		let document = Document::new();
119		let document_rels = DocumentRels::new();
120		let settings = Settings::new();
121		let font_table = FontTable::new();
122		let numberings = Numberings::new();
123		let media = vec![];
124		let web_settings = WebSettings::new();
125
126		Docx {
127			content_type,
128			rels,
129			document_rels,
130			doc_props,
131			styles,
132			document,
133			numberings,
134			settings,
135			font_table,
136			media,
137			web_settings,
138			taskpanes: None,
139			taskpanes_rels: TaskpanesRels::new(),
140			web_extensions: vec![],
141			custom_item_props: vec![],
142			custom_item_rels: vec![],
143			themes: vec![],
144			images: vec![],
145			hyperlinks: vec![],
146		}
147	}
148}
149
150impl Docx {
151	pub fn new() -> Docx {
152		Default::default()
153	}
154
155	pub fn document(mut self, d: Document) -> Docx {
156		for child in &self.document.children {
157			match child {
158				DocumentChild::Paragraph(paragraph) => {
159					if paragraph.has_numbering {
160						self.document_rels.has_numberings = true;
161					}
162				}
163				DocumentChild::Table(table) => {
164					if table.has_numbering {
165						self.document_rels.has_numberings = true;
166					}
167				}
168				_ => {}
169			}
170		}
171		self.document = d;
172		self
173	}
174
175	pub fn styles(mut self, s: Styles) -> Self {
176		self.styles = s;
177		self
178	}
179
180	pub fn add_style(mut self, s: Style) -> Self {
181		self.styles = self.styles.add_style(s);
182		self
183	}
184
185	pub fn numberings(mut self, n: Numberings) -> Self {
186		self.numberings = n;
187		self
188	}
189
190	pub fn settings(mut self, s: Settings) -> Self {
191		self.settings = s;
192		self
193	}
194
195	// reader only
196	pub(crate) fn web_settings(mut self, s: WebSettings) -> Self {
197		self.web_settings = s;
198		self
199	}
200
201	// reader only
202	pub(crate) fn add_image(
203		mut self,
204		id: impl Into<String>,
205		path: impl Into<String>,
206		buf: Vec<u8>,
207	) -> Self {
208		if let Ok(dimg) = image::load_from_memory(&buf) {
209			let mut png = std::io::Cursor::new(vec![]);
210			// For now only png supported
211			dimg.write_to(&mut png, ImageFormat::Png)
212				.expect("Unable to write dynamic image");
213
214			self.images
215				.push((id.into(), path.into(), Image(buf), Png(png.into_inner())));
216		}
217		self
218	}
219
220	// reader only
221	pub(crate) fn add_hyperlink(
222		mut self,
223		id: impl Into<String>,
224		path: impl Into<String>,
225		r#type: impl Into<String>,
226	) -> Self {
227		self.hyperlinks
228			.push((id.into(), path.into(), r#type.into()));
229		self
230	}
231
232	pub fn add_paragraph(mut self, p: Paragraph) -> Docx {
233		if p.has_numbering {
234			// If this document has numbering, set numberings.xml to document_rels.
235			// This is because numberings.xml without numbering cause an error on word online.
236			self.document_rels.has_numberings = true;
237		}
238		self.document = self.document.add_paragraph(p);
239		self
240	}
241
242	pub fn add_structured_data_tag(mut self, t: StructuredDataTag) -> Docx {
243		if t.has_numbering {
244			// If this document has numbering, set numberings.xml to document_rels.
245			// This is because numberings.xml without numbering cause an error on word online.
246			self.document_rels.has_numberings = true;
247		}
248		self.document = self.document.add_structured_data_tag(t);
249		self
250	}
251
252	pub fn add_table_of_contents(mut self, t: TableOfContents) -> Docx {
253		self.document = self.document.add_table_of_contents(t);
254		self
255	}
256
257	pub fn add_table(mut self, t: Table) -> Docx {
258		if t.has_numbering {
259			// If this document has numbering, set numberings.xml to document_rels.
260			// This is because numberings.xml without numbering cause an error on word online.
261			self.document_rels.has_numberings = true;
262		}
263		self.document = self.document.add_table(t);
264		self
265	}
266
267	pub fn header(mut self, header: Header) -> Self {
268		if header.has_numbering {
269			self.document_rels.has_numberings = true;
270		}
271		let count = self.document_rels.header_count + 1;
272		self.document.section_property = self
273			.document
274			.section_property
275			.header(header, &create_header_rid(count));
276		self.document_rels.header_count = count;
277		self.content_type = self.content_type.add_header();
278		self
279	}
280
281	pub fn first_header(mut self, header: Header) -> Self {
282		if header.has_numbering {
283			self.document_rels.has_numberings = true;
284		}
285		let count = self.document_rels.header_count + 1;
286		self.document.section_property = self
287			.document
288			.section_property
289			.first_header(header, &create_header_rid(count));
290		self.document_rels.header_count = count;
291		self.content_type = self.content_type.add_header();
292		self
293	}
294
295	pub fn even_header(mut self, header: Header) -> Self {
296		if header.has_numbering {
297			self.document_rels.has_numberings = true;
298		}
299		let count = self.document_rels.header_count + 1;
300		self.document.section_property = self
301			.document
302			.section_property
303			.even_header(header, &create_header_rid(count));
304		self.document_rels.header_count = count;
305		self.content_type = self.content_type.add_header();
306		self.settings = self.settings.even_and_odd_headers();
307		self
308	}
309
310	pub fn footer(mut self, footer: Footer) -> Self {
311		if footer.has_numbering {
312			self.document_rels.has_numberings = true;
313		}
314		let count = self.document_rels.footer_count + 1;
315		self.document.section_property = self
316			.document
317			.section_property
318			.footer(footer, &create_footer_rid(count));
319		self.document_rels.footer_count = count;
320		self.content_type = self.content_type.add_footer();
321		self
322	}
323
324	pub fn first_footer(mut self, footer: Footer) -> Self {
325		if footer.has_numbering {
326			self.document_rels.has_numberings = true;
327		}
328		let count = self.document_rels.footer_count + 1;
329		self.document.section_property = self
330			.document
331			.section_property
332			.first_footer(footer, &create_footer_rid(count));
333		self.document_rels.footer_count = count;
334		self.content_type = self.content_type.add_footer();
335		self
336	}
337
338	pub fn even_footer(mut self, footer: Footer) -> Self {
339		if footer.has_numbering {
340			self.document_rels.has_numberings = true;
341		}
342		let count = self.document_rels.footer_count + 1;
343		self.document.section_property = self
344			.document
345			.section_property
346			.even_footer(footer, &create_footer_rid(count));
347		self.document_rels.footer_count = count;
348		self.content_type = self.content_type.add_footer();
349		self.settings = self.settings.even_and_odd_headers();
350		self
351	}
352
353	pub fn add_abstract_numbering(mut self, num: AbstractNumbering) -> Docx {
354		self.numberings = self.numberings.add_abstract_numbering(num);
355		self
356	}
357
358	pub fn add_numbering(mut self, num: Numbering) -> Docx {
359		self.numberings = self.numberings.add_numbering(num);
360		self
361	}
362
363	pub fn created_at(mut self, date: &str) -> Self {
364		self.doc_props = self.doc_props.created_at(date);
365		self
366	}
367
368	pub fn updated_at(mut self, date: &str) -> Self {
369		self.doc_props = self.doc_props.updated_at(date);
370		self
371	}
372
373	pub fn custom_property(mut self, name: impl Into<String>, item: impl Into<String>) -> Self {
374		self.doc_props = self.doc_props.custom_property(name, item);
375		self
376	}
377
378	pub fn doc_id(mut self, id: &str) -> Self {
379		self.settings = self.settings.doc_id(id);
380		self
381	}
382
383	pub fn default_tab_stop(mut self, stop: usize) -> Self {
384		self.settings = self.settings.default_tab_stop(stop);
385		self
386	}
387
388	pub fn add_doc_var(mut self, name: &str, val: &str) -> Self {
389		self.settings = self.settings.add_doc_var(name, val);
390		self
391	}
392
393	pub fn page_size(mut self, w: u32, h: u32) -> Self {
394		self.document = self.document.page_size(PageSize::new().size(w, h));
395		self
396	}
397
398	pub fn page_margin(mut self, margin: crate::types::PageMargin) -> Self {
399		self.document = self.document.page_margin(margin);
400		self
401	}
402
403	pub fn page_orient(mut self, o: crate::types::PageOrientationType) -> Self {
404		self.document = self.document.page_orient(o);
405		self
406	}
407
408	pub fn default_size(mut self, size: usize) -> Self {
409		self.styles = self.styles.default_size(size);
410		self
411	}
412
413	pub fn default_spacing(mut self, spacing: i32) -> Self {
414		self.styles = self.styles.default_spacing(spacing);
415		self
416	}
417
418	pub fn default_fonts(mut self, font: RunFonts) -> Self {
419		self.styles = self.styles.default_fonts(font);
420		self
421	}
422
423	pub fn taskpanes(mut self) -> Self {
424		self.taskpanes = Some(Taskpanes::new());
425		self.rels = self.rels.add_taskpanes_rel();
426		self.content_type = self.content_type.add_taskpanes();
427		self
428	}
429
430	pub fn web_extension(mut self, ext: WebExtension) -> Self {
431		self.web_extensions.push(ext);
432		self.taskpanes_rels = self.taskpanes_rels.add_rel();
433		self.content_type = self.content_type.add_web_extensions();
434		self
435	}
436}