1#[cfg(feature = "async")]
2use async_zip::{Compression, ZipEntryBuilder};
3#[cfg(feature = "async")]
4use futures_io::{AsyncBufRead, AsyncWrite};
5use hard_xml::{XmlRead, XmlWrite, XmlWriter};
6use std::borrow::Cow;
7use std::collections::HashMap;
8use std::fs::File;
9use std::io::{Read, Seek, Write};
10use std::path::Path;
11use zip::write::SimpleFileOptions;
12use zip::{result::ZipError, CompressionMethod, ZipArchive, ZipWriter};
13
14use crate::document::{Comments, EndNotes, FootNotes, Footer, Header, Numbering, Theme};
15use crate::media::MediaType;
16use crate::schema::{
17 SCHEMA_COMMENTS, SCHEMA_ENDNOTES, SCHEMA_FOOTNOTES, SCHEMA_HEADER, SCHEMA_NUMBERING,
18 SCHEMA_SETTINGS, SCHEMA_THEME, SCHEMA_WEB_SETTINGS,
19};
20use crate::settings::Settings;
21use crate::web_settings::WebSettings;
22use crate::{
23 app::App,
24 content_type::ContentTypes,
25 core::Core,
26 document::Document,
27 error::DocxResult,
28 font_table::FontTable,
29 rels::Relationships,
30 schema::{
31 SCHEMA_CORE, SCHEMA_FONT_TABLE, SCHEMA_OFFICE_DOCUMENT, SCHEMA_REL_EXTENDED, SCHEMA_STYLES,
32 },
33 styles::Styles,
34};
35
36#[derive(Debug, Default, Clone)]
38pub struct Docx<'a> {
39 pub app: Option<App<'a>>,
41 pub core: Option<Core<'a>>,
43 pub content_types: ContentTypes<'a>,
45 pub document: Document<'a>,
47 pub font_table: Option<FontTable<'a>>,
49 pub styles: Styles<'a>,
51 pub rels: Relationships<'a>,
53 pub document_rels: Option<Relationships<'a>>,
55 pub settings_rels: Option<Relationships<'a>>,
56 pub headers: HashMap<String, Header<'a>>,
57 pub footers: HashMap<String, Footer<'a>>,
58 pub themes: HashMap<String, Theme<'a>>,
59 pub media: HashMap<String, (MediaType, &'a Vec<u8>)>,
60 pub footnotes: Option<FootNotes<'a>>,
61 pub endnotes: Option<EndNotes<'a>>,
62 pub settings: Option<Settings<'a>>,
63 pub web_settings: Option<WebSettings>,
64 pub comments: Option<Comments<'a>>,
65 pub numbering: Option<Numbering<'a>>,
66 pub custom_xml: HashMap<String, Cow<'a, [u8]>>,
67}
68
69impl<'a> Docx<'a> {
70 pub fn write<W: Write + Seek>(&'a mut self, writer: W) -> DocxResult<W> {
71 let mut writer = XmlWriter::new(ZipWriter::new(writer));
72
73 let opt = SimpleFileOptions::default()
74 .compression_method(CompressionMethod::Deflated)
75 .unix_permissions(0o755);
76
77 if self.app.is_some() {
80 self.rels.add_rel(SCHEMA_REL_EXTENDED, "docProps/app.xml");
81 }
82
83 if self.core.is_some() {
84 self.rels.add_rel(SCHEMA_CORE, "docProps/core.xml");
85 }
86
87 self.rels
88 .add_rel(SCHEMA_OFFICE_DOCUMENT, "word/document.xml");
89
90 self.document_rels
91 .get_or_insert(Relationships::default())
92 .add_rel(SCHEMA_STYLES, "styles.xml");
93
94 if self.font_table.is_some() {
95 self.document_rels
96 .get_or_insert(Relationships::default())
97 .add_rel(SCHEMA_FONT_TABLE, "fontTable.xml");
98 }
99
100 if self.footnotes.is_some() {
101 self.document_rels
102 .get_or_insert(Relationships::default())
103 .add_rel(SCHEMA_FOOTNOTES, "footnotes.xml");
104 }
105
106 if self.endnotes.is_some() {
107 self.document_rels
108 .get_or_insert(Relationships::default())
109 .add_rel(SCHEMA_ENDNOTES, "endnotes.xml");
110 }
111
112 if self.settings.is_some() {
113 self.document_rels
114 .get_or_insert(Relationships::default())
115 .add_rel(SCHEMA_SETTINGS, "settings.xml");
116 }
117
118 if self.web_settings.is_some() {
119 self.document_rels
120 .get_or_insert(Relationships::default())
121 .add_rel(SCHEMA_WEB_SETTINGS, "webSettings.xml");
122 }
123
124 if self.comments.is_some() {
125 self.document_rels
126 .get_or_insert(Relationships::default())
127 .add_rel(SCHEMA_COMMENTS, "comments.xml");
128 }
129
130 if self.numbering.is_some() {
131 self.document_rels
132 .get_or_insert(Relationships::default())
133 .add_rel(SCHEMA_NUMBERING, "numbering.xml");
134 }
135
136 for hd in &self.headers {
137 self.document_rels
138 .get_or_insert(Relationships::default())
139 .add_rel(SCHEMA_HEADER, hd.0);
140 }
141
142 for ft in &self.footers {
143 self.document_rels
144 .get_or_insert(Relationships::default())
145 .add_rel(SCHEMA_HEADER, ft.0);
146 }
147
148 for theme in &self.themes {
149 self.document_rels
150 .get_or_insert(Relationships::default())
151 .add_rel(SCHEMA_THEME, theme.0);
152 }
153
154 for media in &self.media {
155 let rel = crate::media::get_media_type_relation_type(&media.1 .0);
156 self.document_rels
157 .get_or_insert(Relationships::default())
158 .add_rel(rel, media.0);
159 }
160
161 macro_rules! write_xml {
164 (Some($xml:expr) => $name:tt) => {
165 if let Some(ref xml) = $xml {
166 write_xml!(xml => $name);
167 }
168 };
169 (Some($xml:expr) => $name:tt $($rest:tt)*) => {
170 write_xml!(Some($xml) => $name);
171 write_xml!($($rest)*);
172 };
173 ($xml:expr => $name:tt) => {
174 writer.inner.start_file($name, opt)?;
175 $xml.to_writer(&mut writer)?;
176 };
177 ($xml:expr => $name:tt $($rest:tt)*) => {
178 write_xml!($xml => $name);
179 write_xml!($($rest)*);
180 };
181 }
182
183 write_xml!(
184 self.content_types => "[Content_Types].xml"
185 Some(self.app) => "docProps/app.xml"
186 Some(self.core) => "docProps/core.xml"
187 self.rels => "_rels/.rels"
188 self.document => "word/document.xml"
189 self.styles => "word/styles.xml"
190 Some(self.font_table) => "word/fontTable.xml"
191 Some(self.footnotes) => "word/footnotes.xml"
192 Some(self.endnotes) => "word/endnotes.xml"
193 Some(self.settings) => "word/settings.xml"
194 Some(self.web_settings) => "word/webSettings.xml"
195 Some(self.comments) => "word/comments.xml"
196 Some(self.numbering) => "word/numbering.xml"
197 Some(self.document_rels) => "word/_rels/document.xml.rels"
198 Some(self.settings_rels) => "word/_rels/settings.xml.rels"
199 );
200
201 for hd in self.headers.iter() {
202 let file_path = format!("word/{}", hd.0);
203 let content = hd.1;
204 write_xml!(
205 content => file_path
206 );
207 }
208
209 for hd in self.footers.iter() {
210 let file_path = format!("word/{}", hd.0);
211 let content = hd.1;
212 write_xml!(
213 content => file_path
214 );
215 }
216
217 for theme in self.themes.iter() {
218 let file_path = format!("word/{}", theme.0);
219 let content = theme.1;
220 write_xml!(
221 content => file_path
222 );
223 }
224
225 for media in self.media.iter() {
226 let file_path = format!("word/{}", media.0);
227 writer.inner.start_file(file_path, opt)?;
228 writer.inner.write_all(media.1 .1)?;
229 }
230
231 for (file_path, content) in &self.custom_xml {
232 writer.inner.start_file(file_path.clone(), opt)?;
233 writer.inner.write_all(content)?;
234 }
235
236 Ok(writer.inner.finish()?)
237 }
238
239 pub fn write_file<P: AsRef<Path>>(&'a mut self, path: P) -> DocxResult<File> {
240 if let Some(p) = path.as_ref().parent() {
241 std::fs::create_dir_all(p)?;
242 }
243 let file = File::create(path)?;
244 self.write(file)
245 }
246}
247
248#[cfg(feature = "async")]
249impl<'a> Docx<'a> {
250 pub async fn write_async<W: AsyncWrite + Unpin>(&'a mut self, writer: W) -> DocxResult<W> {
251 use async_zip::base::write::ZipFileWriter;
252
253 let mut writer = ZipFileWriter::new(writer);
254
255 if self.app.is_some() {
258 self.rels.add_rel(SCHEMA_REL_EXTENDED, "docProps/app.xml");
259 }
260
261 if self.core.is_some() {
262 self.rels.add_rel(SCHEMA_CORE, "docProps/core.xml");
263 }
264
265 self.rels
266 .add_rel(SCHEMA_OFFICE_DOCUMENT, "word/document.xml");
267
268 self.document_rels
269 .get_or_insert(Relationships::default())
270 .add_rel(SCHEMA_STYLES, "styles.xml");
271
272 if self.font_table.is_some() {
273 self.document_rels
274 .get_or_insert(Relationships::default())
275 .add_rel(SCHEMA_FONT_TABLE, "fontTable.xml");
276 }
277
278 if self.footnotes.is_some() {
279 self.document_rels
280 .get_or_insert(Relationships::default())
281 .add_rel(SCHEMA_FOOTNOTES, "footnotes.xml");
282 }
283
284 if self.endnotes.is_some() {
285 self.document_rels
286 .get_or_insert(Relationships::default())
287 .add_rel(SCHEMA_ENDNOTES, "endnotes.xml");
288 }
289
290 if self.settings.is_some() {
291 self.document_rels
292 .get_or_insert(Relationships::default())
293 .add_rel(SCHEMA_SETTINGS, "settings.xml");
294 }
295
296 if self.web_settings.is_some() {
297 self.document_rels
298 .get_or_insert(Relationships::default())
299 .add_rel(SCHEMA_WEB_SETTINGS, "webSettings.xml");
300 }
301
302 if self.comments.is_some() {
303 self.document_rels
304 .get_or_insert(Relationships::default())
305 .add_rel(SCHEMA_COMMENTS, "comments.xml");
306 }
307
308 if self.numbering.is_some() {
309 self.document_rels
310 .get_or_insert(Relationships::default())
311 .add_rel(SCHEMA_NUMBERING, "numbering.xml");
312 }
313
314 for hd in &self.headers {
315 self.document_rels
316 .get_or_insert(Relationships::default())
317 .add_rel(SCHEMA_HEADER, hd.0);
318 }
319
320 for ft in &self.footers {
321 self.document_rels
322 .get_or_insert(Relationships::default())
323 .add_rel(SCHEMA_HEADER, ft.0);
324 }
325
326 for theme in &self.themes {
327 self.document_rels
328 .get_or_insert(Relationships::default())
329 .add_rel(SCHEMA_THEME, theme.0);
330 }
331
332 for media in &self.media {
333 let rel = crate::media::get_media_type_relation_type(&media.1 .0);
334 self.document_rels
335 .get_or_insert(Relationships::default())
336 .add_rel(rel, media.0);
337 }
338
339 macro_rules! write_xml {
342 (Some($xml:expr) => $name:tt) => {
343 if let Some(ref xml) = $xml {
344 write_xml!(xml => $name);
345 }
346 };
347 (Some($xml:expr) => $name:tt $($rest:tt)*) => {
348 write_xml!(Some($xml) => $name);
349 write_xml!($($rest)*);
350 };
351 ($xml:expr => $name:tt) => {
352 let mut buf = XmlWriter::new(Vec::new());
353 $xml.to_writer(&mut buf)?;
354 let opt = ZipEntryBuilder::new(($name.as_ref() as &str).into(), Compression::Deflate);
355 writer.write_entry_whole(opt, &buf.into_inner()).await?;
356 };
357 ($xml:expr => $name:tt $($rest:tt)*) => {
358 write_xml!($xml => $name);
359 write_xml!($($rest)*);
360 };
361 }
362
363 write_xml!(
364 self.content_types => "[Content_Types].xml"
365 Some(self.app) => "docProps/app.xml"
366 Some(self.core) => "docProps/core.xml"
367 self.rels => "_rels/.rels"
368 self.document => "word/document.xml"
369 self.styles => "word/styles.xml"
370 Some(self.font_table) => "word/fontTable.xml"
371 Some(self.footnotes) => "word/footnotes.xml"
372 Some(self.endnotes) => "word/endnotes.xml"
373 Some(self.settings) => "word/settings.xml"
374 Some(self.web_settings) => "word/webSettings.xml"
375 Some(self.comments) => "word/comments.xml"
376 Some(self.numbering) => "word/numbering.xml"
377 Some(self.document_rels) => "word/_rels/document.xml.rels"
378 Some(self.settings_rels) => "word/_rels/settings.xml.rels"
379 );
380
381 for (filename, content) in self.headers.iter() {
382 let file_path = format!("word/{}", filename);
383 write_xml!(
384 content => file_path
385 );
386 }
387
388 for (filename, content) in self.footers.iter() {
389 let file_path = format!("word/{}", filename);
390 write_xml!(
391 content => file_path
392 );
393 }
394
395 for (filename, content) in self.themes.iter() {
396 let file_path = format!("word/{}", filename);
397 write_xml!(
398 content => file_path
399 );
400 }
401
402 for (filename, (_, content)) in self.media.iter() {
403 let file_path = format!("word/{}", filename);
404 let opt = ZipEntryBuilder::new(file_path.as_str().into(), Compression::Deflate);
405 writer.write_entry_whole(opt, content).await?;
406 }
407
408 for (file_path, content) in &self.custom_xml {
409 let opt = ZipEntryBuilder::new(file_path.as_str().into(), Compression::Deflate);
410 writer.write_entry_whole(opt, &content).await?;
411 }
412
413 Ok(writer.close().await?)
414 }
415}
416
417pub struct DocxFile {
419 app: Option<String>,
420 content_types: String,
421 core: Option<String>,
422 document: String,
423 document_rels: Option<String>,
424 settings_rels: Option<String>,
425 font_table: Option<String>,
426 rels: String,
427 styles: Option<String>,
428 settings: Option<String>,
429 web_settings: Option<String>,
430 headers: Vec<(String, String)>,
431 footers: Vec<(String, String)>,
432 themes: Vec<(String, String)>,
433 medias: Vec<(String, Vec<u8>)>,
434 footnotes: Option<String>,
435 endnotes: Option<String>,
436 comments: Option<String>,
437 numbering: Option<String>,
438 custom_xml: Vec<(String, Vec<u8>)>,
439}
440
441impl DocxFile {
442 pub fn from_reader<T: Read + Seek>(reader: T) -> DocxResult<Self> {
444 let mut zip = ZipArchive::new(reader)?;
445
446 macro_rules! read {
447 ($xml:tt, $name:expr) => {{
448 let mut file = zip.by_name($name)?;
449 let mut buffer = String::new();
450 file.read_to_string(&mut buffer)?;
451 buffer
452 }};
453 }
454
455 macro_rules! option_read {
456 ($xml:tt, $name:expr) => {
457 match zip.by_name($name) {
458 Err(ZipError::FileNotFound) => None,
459 Err(e) => return Err(e.into()),
460 Ok(mut file) => {
461 let mut buffer = String::new();
462 file.read_to_string(&mut buffer)?;
463 Some(buffer)
464 }
465 }
466 };
467 }
468
469 macro_rules! option_read_multiple {
470 ($xml:tt, $name:expr) => {{
471 let names: Vec<_> = zip.file_names().map(|x| x.to_string()).collect();
472 let name_and_value: Vec<_> = names
473 .iter()
474 .filter(|n| n.contains($name))
475 .filter_map(|f| {
476 zip.by_name(f).ok().and_then(|mut file| {
477 let mut buffer = String::new();
478 file.read_to_string(&mut buffer).ok()?;
479 Some((f.to_string(), buffer))
480 })
481 })
482 .collect();
483 name_and_value
484 }};
485 }
486
487 macro_rules! option_read_multiple_files {
488 ($xml:tt, $name:expr) => {{
489 let names: Vec<_> = zip.file_names().map(|x| x.to_string()).collect();
490 let name_and_value: Vec<_> = names
491 .iter()
492 .filter(|n| n.contains($name))
493 .filter_map(|f| {
494 zip.by_name(f).ok().and_then(|mut file| {
495 let mut buffer = Vec::new();
496 file.read_to_end(&mut buffer).ok()?;
497 Some((f.to_string(), buffer))
498 })
499 })
500 .collect();
501 name_and_value
502 }};
503 }
504
505 let app = option_read!(App, "docProps/app.xml");
506 let content_types = read!(ContentTypes, "[Content_Types].xml");
507 let core = option_read!(Core, "docProps/core.xml");
508 let document_rels = option_read!(Relationships, "word/_rels/document.xml.rels");
509 let settings_rels = option_read!(Relationships, "word/_rels/settings.xml.rels");
510 let document = read!(Document, "word/document.xml");
511 let font_table = option_read!(FontTable, "word/fontTable.xml");
512 let rels = read!(Relationships, "_rels/.rels");
513 let styles = option_read!(Styles, "word/styles.xml");
514 let settings = option_read!(Settings, "word/settings.xml");
515 let web_settings = option_read!(WebSettings, "word/webSettings.xml");
516 let footnotes = option_read!(Footnotes, "word/footnotes.xml");
517 let endnotes = option_read!(Endnotes, "word/endnotes.xml");
518 let comments = option_read!(Comments, "word/comments.xml");
519 let numbering = option_read!(Numbering, "word/numbering.xml");
520
521 let headers = option_read_multiple!(Headers, "word/header");
522 let footers = option_read_multiple!(Footers, "word/footer");
523 let themes = option_read_multiple!(Themes, "word/theme/theme");
524 let medias = option_read_multiple_files!(Medias, "word/media");
525 let custom_xml = option_read_multiple_files!(_, "custom");
526
527 Ok(DocxFile {
528 app,
529 content_types,
530 core,
531 document_rels,
532 settings_rels,
533 document,
534 font_table,
535 rels,
536 styles,
537 settings,
538 web_settings,
539 headers,
540 footers,
541 themes,
542 medias,
543 footnotes,
544 endnotes,
545 comments,
546 numbering,
547 custom_xml,
548 })
549 }
550
551 #[inline]
553 pub fn from_file<P: AsRef<Path>>(path: P) -> DocxResult<Self> {
554 Self::from_reader(File::open(path)?)
555 }
556
557 pub fn parse(&self) -> DocxResult<Docx<'_>> {
559 let app = if let Some(content) = &self.app {
560 Some(App::from_str(content)?)
561 } else {
562 None
563 };
564
565 let document = Document::from_str(&self.document)?;
566
567 let mut headers = HashMap::new();
568 for f in self.headers.iter() {
569 let hd = Header::from_str(&f.1)?;
570 let name = f.0.replace("word/", "");
571 headers.insert(name, hd);
572 }
573
574 let mut footers = HashMap::new();
575 for f in self.footers.iter() {
576 let ft = Footer::from_str(&f.1)?;
577 let name = f.0.replace("word/", "");
578 footers.insert(name, ft);
579 }
580
581 let mut media = HashMap::new();
582 for m in self.medias.iter() {
583 let mt = crate::media::get_media_type(&m.0);
584 if let Some(mt) = mt {
585 let name = m.0.replace("word/", "");
586 let m = (mt, &m.1);
587 media.insert(name, m);
588 }
589 }
590
591 let mut themes = HashMap::new();
592 for t in self.themes.iter() {
594 let th = Theme::from_str(&t.1)?;
595 let name = t.0.replace("word/", "");
596 themes.insert(name, th);
597 }
598
599 let content_types = ContentTypes::from_str(&self.content_types)?;
600
601 let core = if let Some(content) = &self.core {
602 Some(Core::from_str(content)?)
603 } else {
604 None
605 };
606
607 let document_rels: Option<Relationships> = if let Some(content) = &self.document_rels {
608 Some(Relationships::from_str(content)?)
609 } else {
610 None
611 };
612 let document_rels = document_rels.map(|rel: Relationships| {
613 let rrr: Vec<_> = rel
614 .relationships
615 .iter()
616 .filter(|r2| {
617 matches!(
618 r2.ty.to_string().as_str(),
619 crate::schema::SCHEMA_HEADER
620 | crate::schema::SCHEMA_FOOTER
621 | crate::schema::SCHEMA_THEME
622 | crate::schema::SCHEMA_FONT_TABLE
623 | crate::schema::SCHEMA_STYLES
624 | crate::schema::SCHEMA_FOOTNOTES
625 | crate::schema::SCHEMA_ENDNOTES
626 | crate::schema::SCHEMA_SETTINGS
627 | crate::schema::SCHEMA_WEB_SETTINGS
628 | crate::schema::SCHEMA_COMMENTS
629 | crate::schema::SCHEMA_IMAGE
630 | crate::schema::SCHEMA_HYPERLINK
631 | crate::schema::SCHEMA_NUMBERING
632 )
633 })
634 .map(|d| d.to_owned())
635 .collect();
636 Relationships { relationships: rrr }
637 });
638
639 let settings_rels = self
640 .settings_rels
641 .as_deref()
642 .map(Relationships::from_str)
643 .transpose()?;
644
645 let font_table = if let Some(content) = &self.font_table {
646 Some(FontTable::from_str(content)?)
647 } else {
648 None
649 };
650
651 let footnotes = if let Some(content) = &self.footnotes {
652 Some(FootNotes::from_str(content)?)
653 } else {
654 None
655 };
656
657 let endnotes = if let Some(content) = &self.endnotes {
658 Some(EndNotes::from_str(content)?)
659 } else {
660 None
661 };
662
663 let settings = if let Some(content) = &self.settings {
664 Some(Settings::from_str(content)?)
665 } else {
666 None
667 };
668
669 let web_settings = if let Some(content) = &self.web_settings {
670 Some(WebSettings::from_str(
671 &content.replace("ns0:", "w:").to_string(),
672 )?)
673 } else {
674 None
675 };
676
677 let comments = if let Some(content) = &self.comments {
678 Some(Comments::from_str(content)?)
679 } else {
680 None
681 };
682
683 let numbering = if let Some(content) = &self.numbering {
684 Some(Numbering::from_str(content)?)
685 } else {
686 None
687 };
688
689 let rels = Relationships::from_str(&self.rels)?;
690 let rels = {
691 let rrr: Vec<_> = rels
692 .relationships
693 .iter()
694 .filter(|r2| {
695 matches!(
696 r2.ty.to_string().as_str(),
697 crate::schema::SCHEMA_CORE
698 | crate::schema::SCHEMA_REL_EXTENDED
699 | crate::schema::SCHEMA_OFFICE_DOCUMENT
700 )
701 })
702 .map(|d| d.to_owned())
703 .collect();
704 Relationships { relationships: rrr }
705 };
706
707 let styles = self
708 .styles
709 .as_ref()
710 .map(|content| Styles::from_str(content))
711 .transpose()?
712 .unwrap_or_default();
713
714 let custom_xml = self
715 .custom_xml
716 .iter()
717 .map(|(name, content)| (name.to_string(), Cow::Borrowed(content.as_slice())))
718 .collect();
719
720 Ok(Docx {
721 app,
722 content_types,
723 core,
724 document,
725 document_rels,
726 settings_rels,
727 font_table,
728 rels,
729 styles,
730 headers,
731 footers,
732 themes,
733 media,
734 footnotes,
735 endnotes,
736 settings,
737 web_settings,
738 comments,
739 numbering,
740 custom_xml,
741 })
742 }
743}
744
745#[cfg(feature = "async")]
746impl DocxFile {
747 #[inline]
748 pub async fn from_async_reader<T: AsyncBufRead + Unpin>(reader: T) -> DocxResult<Self> {
749 use async_zip::base::read::stream::ZipFileReader;
750 let mut reader = ZipFileReader::new(reader);
751
752 let mut docx = DocxFile {
753 app: None,
754 content_types: String::new(),
755 core: None,
756 document: String::new(),
757 document_rels: None,
758 settings_rels: None,
759 font_table: None,
760 rels: String::new(),
761 styles: None,
762 settings: None,
763 web_settings: None,
764 headers: vec![],
765 footers: vec![],
766 themes: vec![],
767 medias: vec![],
768 footnotes: None,
769 endnotes: None,
770 comments: None,
771 numbering: None,
772 custom_xml: vec![],
773 };
774
775 while let Some(mut next) = reader.next_with_entry().await? {
776 let entry_reader = next.reader_mut();
777 let filename = entry_reader.entry().filename().as_str()?.to_string();
778
779 macro_rules! read_to_string {
780 ($field:expr) => {{
781 let mut buffer = String::new();
782 entry_reader.read_to_string_checked(&mut buffer).await?;
783 $field = buffer.into();
784 }};
785 }
786
787 macro_rules! read_multiple_to_string {
788 ($field:expr) => {{
789 let mut buffer = String::new();
790 entry_reader.read_to_string_checked(&mut buffer).await?;
791 $field.push((filename, buffer));
792 }};
793 }
794
795 macro_rules! read_multiple_to_bytes {
796 ($field:expr) => {{
797 let mut buffer = Vec::new();
798 entry_reader.read_to_end_checked(&mut buffer).await?;
799 $field.push((filename, buffer));
800 }};
801 }
802
803 match filename.as_str() {
804 "docProps/app.xml" => read_to_string!(docx.app),
805 "[Content_Types].xml" => read_to_string!(docx.content_types),
806 "docProps/core.xml" => read_to_string!(docx.core),
807 "word/_rels/document.xml.rels" => read_to_string!(docx.document_rels),
808 "word/_rels/settings.xml.rels" => read_to_string!(docx.settings_rels),
809 "word/document.xml" => read_to_string!(docx.document),
810 "word/fontTable.xml" => read_to_string!(docx.font_table),
811 "_rels/.rels" => read_to_string!(docx.rels),
812 "word/styles.xml" => read_to_string!(docx.styles),
813 "word/settings.xml" => read_to_string!(docx.settings),
814 "word/webSettings.xml" => read_to_string!(docx.web_settings),
815 "word/footnotes.xml" => read_to_string!(docx.footnotes),
816 "word/endnotes.xml" => read_to_string!(docx.endnotes),
817 "word/comments.xml" => read_to_string!(docx.comments),
818 "word/numbering.xml" => read_to_string!(docx.numbering),
819 _ if filename.contains("word/header") => read_multiple_to_string!(docx.headers),
820 _ if filename.contains("word/footer") => read_multiple_to_string!(docx.footers),
821 _ if filename.contains("word/theme/theme") => {
822 read_multiple_to_string!(docx.themes)
823 }
824 _ if filename.contains("word/media") => read_multiple_to_bytes!(docx.medias),
825 _ if filename.contains("custom") => read_multiple_to_bytes!(docx.custom_xml),
826 _ => {}
827 }
828 reader = next.skip().await?;
829 }
830
831 Ok(docx)
832 }
833}