Skip to main content

docx_rs/documents/
mod.rs

1use std::{
2    collections::{HashMap, HashSet},
3    str::FromStr,
4};
5
6mod bookmark_id;
7mod build_xml;
8mod comments;
9mod comments_extended;
10mod content_types;
11mod custom_item;
12mod custom_item_property;
13mod custom_item_rels;
14mod doc_props;
15mod document;
16mod document_rels;
17mod elements;
18mod font_table;
19mod footer;
20mod footer_id;
21mod footer_rels;
22mod footnote_id;
23mod footnotes;
24mod header;
25mod header_id;
26mod header_rels;
27mod history_id;
28mod hyperlink_id;
29mod image_collector;
30mod numberings;
31mod paragraph_id;
32mod paragraph_property_change_id;
33mod pic_id;
34mod preset_styles;
35mod rels;
36mod settings;
37mod styles;
38mod taskpanes;
39mod taskpanes_rels;
40mod theme;
41mod toc_key;
42mod web_settings;
43mod webextension;
44mod xml_docx;
45
46pub use build_xml::BuildXML;
47pub(crate) use history_id::HistoryId;
48pub(crate) use hyperlink_id::*;
49pub(crate) use paragraph_id::*;
50pub(crate) use paragraph_property_change_id::ParagraphPropertyChangeId;
51pub(crate) use pic_id::*;
52
53pub use bookmark_id::*;
54pub use comments::*;
55pub use comments_extended::*;
56pub use content_types::*;
57pub use custom_item::*;
58pub use custom_item_property::*;
59pub use custom_item_rels::*;
60pub use doc_props::*;
61pub use document::*;
62pub use document_rels::*;
63pub use elements::*;
64pub use font_table::*;
65pub use footer::*;
66pub use footer_id::*;
67pub use footer_rels::*;
68pub use footnotes::*;
69pub use header::*;
70pub use header_id::*;
71pub use header_rels::*;
72pub use numberings::*;
73pub use rels::*;
74pub use settings::*;
75pub use styles::*;
76pub use taskpanes::*;
77pub use taskpanes_rels::*;
78pub use theme::*;
79pub use toc_key::*;
80pub use web_settings::*;
81pub use webextension::*;
82pub use xml_docx::*;
83
84use base64::Engine;
85use serde::{ser, Serialize};
86
87use self::image_collector::{collect_images_from_paragraph, collect_images_from_table};
88
89#[derive(Debug, Clone)]
90pub struct Image(pub Vec<u8>);
91
92/// Decoded preview bytes for an entry in `Docx.images`.
93///
94/// For raster originals (PNG / JPEG / BMP / GIF / TIFF) decoded via the
95/// `image` crate, the contents are PNG bytes. Unsupported formats such as
96/// EMF are surfaced with an empty preview so downstream consumers can
97/// provide their own conversion. The struct is named `Png` for backwards
98/// compatibility.
99#[derive(Debug, Clone)]
100pub struct Png(pub Vec<u8>);
101
102pub type ImageIdAndPath = (String, String);
103pub type ImageIdAndBuf = (String, Vec<u8>);
104
105fn is_emf(path: &str, buf: &[u8]) -> bool {
106    if path.to_ascii_lowercase().ends_with(".emf") {
107        return true;
108    }
109    // EMF files start with EMR_HEADER, whose first 4 bytes are the
110    // record type 0x00000001 (little-endian) and bytes 40..44 hold
111    // the signature " EMF" (0x464D4520).
112    buf.len() >= 44
113        && buf[0..4] == [0x01, 0x00, 0x00, 0x00]
114        && buf[40..44] == [0x20, 0x45, 0x4D, 0x46]
115}
116
117impl ser::Serialize for Image {
118    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
119    where
120        S: ser::Serializer,
121    {
122        let base64 = base64::engine::general_purpose::STANDARD.encode(&self.0);
123        serializer.collect_str(&base64)
124    }
125}
126
127impl ser::Serialize for Png {
128    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
129    where
130        S: ser::Serializer,
131    {
132        let base64 = base64::engine::general_purpose::STANDARD.encode(&self.0);
133        serializer.collect_str(&base64)
134    }
135}
136
137#[derive(Debug, Clone, Serialize)]
138#[serde(rename_all = "camelCase")]
139pub struct Docx {
140    pub content_type: ContentTypes,
141    pub rels: Rels,
142    pub document_rels: DocumentRels,
143    pub doc_props: DocProps,
144    pub styles: Styles,
145    pub document: Document,
146    pub comments: Comments,
147    pub numberings: Numberings,
148    pub settings: Settings,
149    pub font_table: FontTable,
150    pub media: Vec<(String, Vec<u8>)>,
151    pub comments_extended: CommentsExtended,
152    pub web_settings: WebSettings,
153    pub taskpanes: Option<Taskpanes>,
154    pub taskpanes_rels: TaskpanesRels,
155    pub web_extensions: Vec<WebExtension>,
156    pub custom_items: Vec<CustomItem>,
157    pub custom_item_props: Vec<CustomItemProperty>,
158    pub custom_item_rels: Vec<CustomItemRels>,
159    // reader only
160    pub themes: Vec<Theme>,
161    /// Reader-only collection of images embedded in `word/media/`.
162    ///
163    /// Each tuple is `(rId, media path, original bytes, preview bytes)`.
164    /// The preview is PNG for raster originals decoded via the `image`
165    /// crate. Unsupported formats such as EMF keep an empty preview for
166    /// downstream consumers to populate. See [`Png`] for details.
167    pub images: Vec<(String, String, Image, Png)>,
168    // reader only
169    pub hyperlinks: Vec<(String, String, String)>,
170    pub footnotes: Footnotes,
171}
172
173impl Default for Docx {
174    fn default() -> Self {
175        let content_type = ContentTypes::new().set_default();
176        let rels = Rels::new().set_default();
177        let doc_props = DocProps::new(CorePropsConfig::new());
178        let styles = Styles::new();
179        let document = Document::new();
180        let document_rels = DocumentRels::new();
181        let settings = Settings::new();
182        let font_table = FontTable::new();
183        let comments = Comments::new();
184        let numberings = Numberings::new();
185        let media = vec![];
186        let comments_extended = CommentsExtended::new();
187        let web_settings = WebSettings::new();
188        let footnotes = Footnotes::default();
189
190        Docx {
191            content_type,
192            rels,
193            document_rels,
194            doc_props,
195            styles,
196            document,
197            comments,
198            numberings,
199            settings,
200            font_table,
201            media,
202            comments_extended,
203            web_settings,
204            taskpanes: None,
205            taskpanes_rels: TaskpanesRels::new(),
206            web_extensions: vec![],
207            custom_items: vec![],
208            custom_item_props: vec![],
209            custom_item_rels: vec![],
210            themes: vec![],
211            images: vec![],
212            hyperlinks: vec![],
213            footnotes,
214        }
215    }
216}
217
218impl Docx {
219    pub fn new() -> Docx {
220        Default::default()
221    }
222
223    pub fn document(mut self, d: Document) -> Docx {
224        for child in &self.document.children {
225            match child {
226                DocumentChild::Paragraph(paragraph) => {
227                    if paragraph.has_numbering {
228                        self.document_rels.has_numberings = true;
229                    }
230                }
231                DocumentChild::Table(table) if table.has_numbering => {
232                    self.document_rels.has_numberings = true;
233                }
234                _ => {}
235            }
236        }
237        self.document = d;
238        self
239    }
240
241    pub fn styles(mut self, s: Styles) -> Self {
242        self.styles = s;
243        self
244    }
245
246    pub fn add_style(mut self, s: Style) -> Self {
247        self.styles = self.styles.add_style(s);
248        self
249    }
250
251    pub fn numberings(mut self, n: Numberings) -> Self {
252        self.numberings = n;
253        self
254    }
255
256    pub fn settings(mut self, s: Settings) -> Self {
257        self.settings = s;
258        self
259    }
260
261    // reader only
262    pub(crate) fn web_settings(mut self, s: WebSettings) -> Self {
263        self.web_settings = s;
264        self
265    }
266
267    // reader only
268    pub(crate) fn add_image(
269        mut self,
270        id: impl Into<String>,
271        path: impl Into<String>,
272        buf: Vec<u8>,
273    ) -> Self {
274        let path: String = path.into();
275
276        if is_emf(&path, &buf) {
277            self.images
278                .push((id.into(), path, Image(buf), Png(Vec::new())));
279            return self;
280        }
281
282        #[cfg(feature = "image")]
283        if let Ok(dimg) = image::load_from_memory(&buf) {
284            let mut png = std::io::Cursor::new(vec![]);
285            // For now only png supported
286            dimg.write_to(&mut png, image::ImageFormat::Png)
287                .expect("Unable to write dynamic image");
288
289            self.images
290                .push((id.into(), path, Image(buf), Png(png.into_inner())));
291        }
292        #[cfg(not(feature = "image"))]
293        // without 'image' crate we can only test for PNG file signature
294        if buf.starts_with(&[137, 80, 78, 71, 13, 10, 26, 10]) {
295            self.images
296                .push((id.into(), path, Image(buf.clone()), Png(buf)));
297        }
298        self
299    }
300
301    // reader only
302    pub(crate) fn add_hyperlink(
303        mut self,
304        id: impl Into<String>,
305        path: impl Into<String>,
306        r#type: impl Into<String>,
307    ) -> Self {
308        self.hyperlinks
309            .push((id.into(), path.into(), r#type.into()));
310        self
311    }
312
313    pub fn comments(mut self, c: Comments) -> Self {
314        self.comments = c;
315        self
316    }
317
318    pub fn comments_extended(mut self, c: CommentsExtended) -> Self {
319        self.comments_extended = c;
320        self
321    }
322
323    pub fn add_paragraph(mut self, p: Paragraph) -> Docx {
324        if p.has_numbering {
325            // If this document has numbering, set numberings.xml to document_rels.
326            // This is because numberings.xml without numbering cause an error on word online.
327            self.document_rels.has_numberings = true;
328        }
329        self.document = self.document.add_paragraph(p);
330        self
331    }
332
333    pub fn add_section(mut self, s: Section) -> Docx {
334        if s.has_numbering {
335            // If this document has numbering, set numberings.xml to document_rels.
336            // This is because numberings.xml without numbering cause an error on word online.
337            self.document_rels.has_numberings = true;
338        }
339
340        let mut new_section = s;
341
342        // header
343        if let Some(header) = new_section.temp_header {
344            if header.has_numbering {
345                self.document_rels.has_numberings = true;
346            }
347            let count = self.document_rels.header_count + 1;
348            new_section = Section {
349                property: new_section
350                    .property
351                    .header(header, &create_header_rid(count)),
352                children: new_section.children,
353                has_numbering: new_section.has_numbering,
354                temp_header: None,
355                ..Default::default()
356            };
357            self.document_rels.header_count = count;
358            self.content_type = self.content_type.add_header();
359        }
360
361        if let Some(header) = new_section.temp_first_header {
362            if header.has_numbering {
363                self.document_rels.has_numberings = true;
364            }
365            let count = self.document_rels.header_count + 1;
366            new_section = Section {
367                property: new_section
368                    .property
369                    .first_header(header, &create_header_rid(count)),
370                children: new_section.children,
371                has_numbering: new_section.has_numbering,
372                temp_first_header: None,
373                ..Default::default()
374            };
375            self.document_rels.header_count = count;
376            self.content_type = self.content_type.add_header();
377        }
378
379        if let Some(header) = new_section.temp_even_header {
380            if header.has_numbering {
381                self.document_rels.has_numberings = true;
382            }
383            let count = self.document_rels.header_count + 1;
384            new_section = Section {
385                property: new_section
386                    .property
387                    .even_header(header, &create_header_rid(count)),
388                children: new_section.children,
389                has_numbering: new_section.has_numbering,
390                temp_even_header: None,
391                ..Default::default()
392            };
393            self.document_rels.header_count = count;
394            self.content_type = self.content_type.add_header();
395        }
396
397        // header
398        if let Some(header) = new_section.temp_header {
399            if header.has_numbering {
400                self.document_rels.has_numberings = true;
401            }
402            let count = self.document_rels.header_count + 1;
403            new_section = Section {
404                property: new_section
405                    .property
406                    .header(header, &create_header_rid(count)),
407                children: new_section.children,
408                has_numbering: new_section.has_numbering,
409                temp_header: None,
410                ..Default::default()
411            };
412            self.document_rels.header_count = count;
413            self.content_type = self.content_type.add_header();
414        }
415
416        if let Some(header) = new_section.temp_first_header {
417            if header.has_numbering {
418                self.document_rels.has_numberings = true;
419            }
420            let count = self.document_rels.header_count + 1;
421            new_section = Section {
422                property: new_section
423                    .property
424                    .first_header(header, &create_header_rid(count)),
425                children: new_section.children,
426                has_numbering: new_section.has_numbering,
427                temp_first_header: None,
428                ..Default::default()
429            };
430            self.document_rels.header_count = count;
431            self.content_type = self.content_type.add_header();
432        }
433
434        if let Some(header) = new_section.temp_even_header {
435            if header.has_numbering {
436                self.document_rels.has_numberings = true;
437            }
438            let count = self.document_rels.header_count + 1;
439            new_section = Section {
440                property: new_section
441                    .property
442                    .even_header(header, &create_header_rid(count)),
443                children: new_section.children,
444                has_numbering: new_section.has_numbering,
445                temp_even_header: None,
446                ..Default::default()
447            };
448            self.document_rels.header_count = count;
449            self.content_type = self.content_type.add_header();
450        }
451
452        // footer
453        if let Some(footer) = new_section.temp_footer {
454            if footer.has_numbering {
455                self.document_rels.has_numberings = true;
456            }
457            let count = self.document_rels.footer_count + 1;
458            new_section = Section {
459                property: new_section
460                    .property
461                    .footer(footer, &create_footer_rid(count)),
462                children: new_section.children,
463                has_numbering: new_section.has_numbering,
464                temp_footer: None,
465                ..Default::default()
466            };
467            self.document_rels.footer_count = count;
468            self.content_type = self.content_type.add_footer();
469        }
470
471        if let Some(footer) = new_section.temp_first_footer {
472            if footer.has_numbering {
473                self.document_rels.has_numberings = true;
474            }
475            let count = self.document_rels.footer_count + 1;
476            new_section = Section {
477                property: new_section
478                    .property
479                    .first_footer(footer, &create_footer_rid(count)),
480                children: new_section.children,
481                has_numbering: new_section.has_numbering,
482                temp_first_footer: None,
483                ..Default::default()
484            };
485            self.document_rels.footer_count = count;
486            self.content_type = self.content_type.add_footer();
487        }
488
489        if let Some(footer) = new_section.temp_even_footer {
490            if footer.has_numbering {
491                self.document_rels.has_numberings = true;
492            }
493            let count = self.document_rels.footer_count + 1;
494            new_section = Section {
495                property: new_section
496                    .property
497                    .even_footer(footer, &create_footer_rid(count)),
498                children: new_section.children,
499                has_numbering: new_section.has_numbering,
500                temp_even_footer: None,
501                ..Default::default()
502            };
503            self.document_rels.footer_count = count;
504            self.content_type = self.content_type.add_footer();
505        }
506
507        self.document = self.document.add_section(new_section);
508        self
509    }
510
511    pub fn add_structured_data_tag(mut self, t: StructuredDataTag) -> Docx {
512        if t.has_numbering {
513            // If this document has numbering, set numberings.xml to document_rels.
514            // This is because numberings.xml without numbering cause an error on word online.
515            self.document_rels.has_numberings = true;
516        }
517        self.document = self.document.add_structured_data_tag(t);
518        self
519    }
520
521    pub fn add_table_of_contents(mut self, t: TableOfContents) -> Docx {
522        self.document = self.document.add_table_of_contents(t);
523        self
524    }
525
526    pub fn add_bookmark_start(mut self, id: usize, name: impl Into<String>) -> Docx {
527        self.document = self.document.add_bookmark_start(id, name);
528        self
529    }
530
531    pub fn add_bookmark_end(mut self, id: usize) -> Docx {
532        self.document = self.document.add_bookmark_end(id);
533        self
534    }
535
536    pub fn add_table(mut self, t: Table) -> Docx {
537        if t.has_numbering {
538            // If this document has numbering, set numberings.xml to document_rels.
539            // This is because numberings.xml without numbering cause an error on word online.
540            self.document_rels.has_numberings = true;
541        }
542        self.document = self.document.add_table(t);
543        self
544    }
545
546    pub fn header(mut self, header: Header) -> Self {
547        if header.has_numbering {
548            self.document_rels.has_numberings = true;
549        }
550        let count = self.document_rels.header_count + 1;
551        self.document.section_property = self
552            .document
553            .section_property
554            .header(header, &create_header_rid(count));
555        self.document_rels.header_count = count;
556        self.content_type = self.content_type.add_header();
557        self
558    }
559
560    pub fn first_header(mut self, header: Header) -> Self {
561        if header.has_numbering {
562            self.document_rels.has_numberings = true;
563        }
564        let count = self.document_rels.header_count + 1;
565        self.document.section_property = self
566            .document
567            .section_property
568            .first_header(header, &create_header_rid(count));
569        self.document_rels.header_count = count;
570        self.content_type = self.content_type.add_header();
571        self
572    }
573
574    pub fn even_header(mut self, header: Header) -> Self {
575        if header.has_numbering {
576            self.document_rels.has_numberings = true;
577        }
578        let count = self.document_rels.header_count + 1;
579        self.document.section_property = self
580            .document
581            .section_property
582            .even_header(header, &create_header_rid(count));
583        self.document_rels.header_count = count;
584        self.content_type = self.content_type.add_header();
585        self.settings = self.settings.even_and_odd_headers();
586        self
587    }
588
589    pub fn footer(mut self, footer: Footer) -> Self {
590        if footer.has_numbering {
591            self.document_rels.has_numberings = true;
592        }
593        let count = self.document_rels.footer_count + 1;
594        self.document.section_property = self
595            .document
596            .section_property
597            .footer(footer, &create_footer_rid(count));
598        self.document_rels.footer_count = count;
599        self.content_type = self.content_type.add_footer();
600        self
601    }
602
603    pub fn first_footer(mut self, footer: Footer) -> Self {
604        if footer.has_numbering {
605            self.document_rels.has_numberings = true;
606        }
607        let count = self.document_rels.footer_count + 1;
608        self.document.section_property = self
609            .document
610            .section_property
611            .first_footer(footer, &create_footer_rid(count));
612        self.document_rels.footer_count = count;
613        self.content_type = self.content_type.add_footer();
614        self
615    }
616
617    pub fn even_footer(mut self, footer: Footer) -> Self {
618        if footer.has_numbering {
619            self.document_rels.has_numberings = true;
620        }
621        let count = self.document_rels.footer_count + 1;
622        self.document.section_property = self
623            .document
624            .section_property
625            .even_footer(footer, &create_footer_rid(count));
626        self.document_rels.footer_count = count;
627        self.content_type = self.content_type.add_footer();
628        self.settings = self.settings.even_and_odd_headers();
629        self
630    }
631
632    pub fn add_abstract_numbering(mut self, num: AbstractNumbering) -> Docx {
633        self.numberings = self.numberings.add_abstract_numbering(num);
634        self
635    }
636
637    pub fn add_numbering(mut self, num: Numbering) -> Docx {
638        self.numberings = self.numberings.add_numbering(num);
639        self
640    }
641
642    pub fn created_at(mut self, date: &str) -> Self {
643        self.doc_props = self.doc_props.created_at(date);
644        self
645    }
646
647    pub fn updated_at(mut self, date: &str) -> Self {
648        self.doc_props = self.doc_props.updated_at(date);
649        self
650    }
651
652    pub fn custom_property(mut self, name: impl Into<String>, item: impl Into<String>) -> Self {
653        self.doc_props = self.doc_props.custom_property(name, item);
654        self
655    }
656
657    pub fn doc_id(mut self, id: &str) -> Self {
658        self.settings = self.settings.doc_id(id);
659        self
660    }
661
662    pub fn default_tab_stop(mut self, stop: usize) -> Self {
663        self.settings = self.settings.default_tab_stop(stop);
664        self
665    }
666
667    pub fn add_doc_var(mut self, name: &str, val: &str) -> Self {
668        self.settings = self.settings.add_doc_var(name, val);
669        self
670    }
671
672    pub fn title_pg(mut self) -> Self {
673        self.document = self.document.title_pg();
674        self
675    }
676
677    pub fn page_size(mut self, w: u32, h: u32) -> Self {
678        self.document = self.document.page_size(PageSize::new().size(w, h));
679        self
680    }
681
682    pub fn page_margin(mut self, margin: crate::types::PageMargin) -> Self {
683        self.document = self.document.page_margin(margin);
684        self
685    }
686
687    pub fn page_orient(mut self, o: crate::types::PageOrientationType) -> Self {
688        self.document = self.document.page_orient(o);
689        self
690    }
691
692    pub fn default_size(mut self, size: usize) -> Self {
693        self.styles = self.styles.default_size(size);
694        self
695    }
696
697    pub fn default_spacing(mut self, spacing: i32) -> Self {
698        self.styles = self.styles.default_spacing(spacing);
699        self
700    }
701
702    pub fn default_fonts(mut self, font: RunFonts) -> Self {
703        self.styles = self.styles.default_fonts(font);
704        self
705    }
706
707    pub fn default_line_spacing(mut self, spacing: LineSpacing) -> Self {
708        self.styles = self.styles.default_line_spacing(spacing);
709        self
710    }
711
712    pub fn taskpanes(mut self) -> Self {
713        self.taskpanes = Some(Taskpanes::new());
714        self.rels = self.rels.add_taskpanes_rel();
715        self.content_type = self.content_type.add_taskpanes();
716        self
717    }
718
719    pub fn web_extension(mut self, ext: WebExtension) -> Self {
720        self.web_extensions.push(ext);
721        self.taskpanes_rels = self.taskpanes_rels.add_rel();
722        self.content_type = self.content_type.add_web_extensions();
723        self
724    }
725
726    pub fn add_custom_item(mut self, id: &str, xml: &str) -> Self {
727        let x = CustomItem::from_str(xml).expect("should parse xml string");
728        self.content_type = self.content_type.add_custom_xml();
729        let rel = CustomItemRels::new().add_item();
730        self.custom_item_props.push(CustomItemProperty::new(id));
731        self.document_rels = self.document_rels.add_custom_item();
732        self.custom_item_rels.push(rel);
733        self.custom_items.push(x);
734        self
735    }
736
737    pub fn page_num_type(mut self, p: PageNumType) -> Self {
738        self.document = self.document.page_num_type(p);
739        self
740    }
741
742    pub fn build(mut self) -> XMLDocx {
743        self.reset();
744        self.refresh_duplicate_para_ids();
745
746        self.update_dependencies();
747
748        let tocs: Vec<(usize, Box<TableOfContents>)> = self
749            .document
750            .children
751            .iter()
752            .enumerate()
753            .filter_map(|(i, child)| {
754                if let DocumentChild::TableOfContents(toc) = child {
755                    Some((i, toc.clone()))
756                } else {
757                    None
758                }
759            })
760            .collect();
761
762        let has_toc = !tocs.is_empty();
763
764        for (i, toc) in tocs {
765            if toc.items.is_empty() && toc.auto {
766                let children =
767                    update_document_by_toc(self.document.children, &self.styles, *toc, i);
768                self.document.children = children;
769            }
770        }
771
772        let (images, mut images_bufs) = self.images_in_doc();
773        let (header_images, header_images_bufs) = self.images_in_header();
774        let (footer_images, footer_images_bufs) = self.images_in_footer();
775
776        images_bufs.extend(header_images_bufs);
777        images_bufs.extend(footer_images_bufs);
778
779        let mut header_rels = vec![HeaderRels::new(); 3];
780        for (i, images) in header_images.iter().enumerate() {
781            if let Some(h) = header_rels.get_mut(i) {
782                h.set_images(images.to_owned());
783            }
784        }
785        let mut footer_rels = vec![FooterRels::new(); 3];
786        for (i, images) in footer_images.iter().enumerate() {
787            if let Some(f) = footer_rels.get_mut(i) {
788                f.set_images(images.to_owned());
789            }
790        }
791
792        let web_extensions = self.web_extensions.iter().map(|ext| ext.build()).collect();
793        let custom_items = self.custom_items.iter().map(|xml| xml.build()).collect();
794        let custom_item_props = self.custom_item_props.iter().map(|p| p.build()).collect();
795        let custom_item_rels = self
796            .custom_item_rels
797            .iter()
798            .map(|rel| rel.build())
799            .collect();
800
801        self.document_rels.images = images;
802
803        let mut headers: Vec<&(String, Header)> = self.document.section_property.get_headers();
804
805        self.document.children.iter().for_each(|child| {
806            if let DocumentChild::Section(section) = child {
807                for h in section.property.get_headers() {
808                    headers.push(h);
809                }
810            }
811        });
812
813        headers.sort_by(|a, b| a.0.cmp(&b.0));
814        let headers = headers.iter().map(|h| h.1.build()).collect();
815
816        let mut footers: Vec<&(String, Footer)> = self.document.section_property.get_footers();
817
818        self.document.children.iter().for_each(|child| {
819            if let DocumentChild::Section(section) = child {
820                for h in section.property.get_footers() {
821                    footers.push(h);
822                }
823            }
824        });
825
826        footers.sort_by(|a, b| a.0.cmp(&b.0));
827        let footers = footers.iter().map(|h| h.1.build()).collect();
828
829        // Collect footnotes
830        if self.collect_footnotes() {
831            // Relationship entry for footnotes
832            self.content_type = self.content_type.add_footnotes();
833            self.document_rels.has_footnotes = true;
834        }
835
836        if has_toc {
837            for i in 1..=9 {
838                if !self
839                    .styles
840                    .styles
841                    .iter()
842                    .any(|s| s.name == Name::new(format!("toc {i}")))
843                {
844                    self.styles = self
845                        .styles
846                        .add_style(crate::documents::preset_styles::toc(i));
847                }
848            }
849        }
850
851        XMLDocx {
852            content_type: self.content_type.build(),
853            rels: self.rels.build(),
854            doc_props: self.doc_props.build(),
855            styles: self.styles.build(),
856            document: self.document.build(),
857            comments: self.comments.build(),
858            document_rels: self.document_rels.build(),
859            header_rels: header_rels.into_iter().map(|r| r.build()).collect(),
860            footer_rels: footer_rels.into_iter().map(|r| r.build()).collect(),
861            settings: self.settings.build(),
862            font_table: self.font_table.build(),
863            numberings: self.numberings.build(),
864            media: images_bufs,
865            headers,
866            footers,
867            comments_extended: self.comments_extended.build(),
868            taskpanes: self.taskpanes.map(|taskpanes| taskpanes.build()),
869            taskpanes_rels: self.taskpanes_rels.build(),
870            web_extensions,
871            custom_items,
872            custom_item_rels,
873            custom_item_props,
874            footnotes: self.footnotes.build(),
875        }
876    }
877
878    pub fn json(&self) -> String {
879        self.reset();
880
881        serde_json::to_string_pretty(&self).unwrap()
882    }
883
884    // Internal: for docx-wasm
885    pub fn json_with_update_comments(&mut self) -> String {
886        self.reset();
887
888        self.update_dependencies();
889        serde_json::to_string_pretty(&self).unwrap()
890    }
891
892    // Internal: for docx-wasm
893    pub fn comments_json(&mut self) -> String {
894        self.reset();
895        self.update_dependencies();
896        serde_json::to_string_pretty(&self.comments).unwrap()
897    }
898
899    fn reset(&self) {
900        crate::reset_para_id();
901    }
902
903    fn refresh_duplicate_para_ids(&mut self) {
904        let mut counts: HashMap<String, usize> = HashMap::new();
905        collect_para_ids_in_docx(self, &mut counts);
906
907        let mut used: HashSet<String> = counts.keys().cloned().collect();
908        let mut seen: HashSet<String> = HashSet::new();
909
910        refresh_para_ids_in_docx(self, &counts, &mut used, &mut seen);
911    }
912
913    fn insert_comment_to_map(
914        &self,
915        comment_map: &mut HashMap<usize, String>,
916        c: &CommentRangeStart,
917    ) {
918        let comment = c.get_comment_ref();
919        let comment_id = comment.id();
920        for child in &comment.children {
921            if let CommentChild::Paragraph(child) = child {
922                let para_id = child.id.clone();
923                comment_map.insert(comment_id, para_id);
924            }
925            // TODO: Support table in comment
926        }
927    }
928
929    // Traverse and clone comments from document and add to comments node.
930    fn update_dependencies(&mut self) {
931        let mut comments: Vec<Comment> = vec![];
932        let mut comments_extended: Vec<CommentExtended> = vec![];
933        let mut comment_map: HashMap<usize, String> = HashMap::new();
934
935        let mut hyperlink_map: HashMap<String, String> = HashMap::new();
936
937        for child in &self.document.children {
938            match child {
939                DocumentChild::Paragraph(paragraph) => {
940                    for child in &paragraph.children {
941                        if let ParagraphChild::CommentStart(c) = child {
942                            self.insert_comment_to_map(&mut comment_map, c);
943                        }
944                        if let ParagraphChild::Hyperlink(h) = child {
945                            for child in &h.children {
946                                if let ParagraphChild::CommentStart(c) = child {
947                                    self.insert_comment_to_map(&mut comment_map, c);
948                                }
949                            }
950                        }
951                    }
952                }
953                DocumentChild::Table(table) => {
954                    collect_comment_map_in_table(table, &mut comment_map);
955                }
956                DocumentChild::TableOfContents(toc) => {
957                    for child in &toc.before_contents {
958                        if let TocContent::Paragraph(paragraph) = child {
959                            collect_comment_map_in_paragraph(paragraph, &mut comment_map);
960                        }
961                        if let TocContent::Table(table) = child {
962                            collect_comment_map_in_table(table, &mut comment_map);
963                        }
964                    }
965                    for child in &toc.after_contents {
966                        if let TocContent::Paragraph(paragraph) = child {
967                            collect_comment_map_in_paragraph(paragraph, &mut comment_map);
968                        }
969                        if let TocContent::Table(table) = child {
970                            collect_comment_map_in_table(table, &mut comment_map);
971                        }
972                    }
973                }
974                _ => {}
975            }
976        }
977
978        for child in &self.document.children {
979            match child {
980                DocumentChild::Paragraph(paragraph) => {
981                    for child in &paragraph.children {
982                        if let ParagraphChild::CommentStart(c) = child {
983                            push_comment_and_comment_extended(
984                                &mut comments,
985                                &mut comments_extended,
986                                &comment_map,
987                                c,
988                            );
989                        }
990                        if let ParagraphChild::Hyperlink(h) = child {
991                            if let HyperlinkData::External { rid, path } = &h.link {
992                                hyperlink_map.insert(rid.clone(), path.clone());
993                            };
994                            for child in &h.children {
995                                if let ParagraphChild::CommentStart(c) = child {
996                                    push_comment_and_comment_extended(
997                                        &mut comments,
998                                        &mut comments_extended,
999                                        &comment_map,
1000                                        c,
1001                                    );
1002                                }
1003                            }
1004                        }
1005                    }
1006                }
1007                DocumentChild::Table(table) => {
1008                    collect_dependencies_in_table(
1009                        table,
1010                        &mut comments,
1011                        &mut comments_extended,
1012                        &mut comment_map,
1013                        &mut hyperlink_map,
1014                    );
1015                }
1016                DocumentChild::TableOfContents(toc) => {
1017                    // TODO:refine later
1018                    for child in &toc.before_contents {
1019                        if let TocContent::Paragraph(paragraph) = child {
1020                            for child in &paragraph.children {
1021                                if let ParagraphChild::CommentStart(c) = child {
1022                                    push_comment_and_comment_extended(
1023                                        &mut comments,
1024                                        &mut comments_extended,
1025                                        &comment_map,
1026                                        c,
1027                                    );
1028                                }
1029                                if let ParagraphChild::Hyperlink(h) = child {
1030                                    if let HyperlinkData::External { rid, path } = &h.link {
1031                                        hyperlink_map.insert(rid.clone(), path.clone());
1032                                    };
1033                                    for child in &h.children {
1034                                        if let ParagraphChild::CommentStart(c) = child {
1035                                            push_comment_and_comment_extended(
1036                                                &mut comments,
1037                                                &mut comments_extended,
1038                                                &comment_map,
1039                                                c,
1040                                            );
1041                                        }
1042                                    }
1043                                }
1044                            }
1045                        }
1046                        if let TocContent::Table(table) = child {
1047                            collect_dependencies_in_table(
1048                                table,
1049                                &mut comments,
1050                                &mut comments_extended,
1051                                &mut comment_map,
1052                                &mut hyperlink_map,
1053                            );
1054                        }
1055                    }
1056                    for child in &toc.after_contents {
1057                        if let TocContent::Paragraph(paragraph) = child {
1058                            for child in &paragraph.children {
1059                                if let ParagraphChild::CommentStart(c) = child {
1060                                    push_comment_and_comment_extended(
1061                                        &mut comments,
1062                                        &mut comments_extended,
1063                                        &comment_map,
1064                                        c,
1065                                    );
1066                                }
1067                                if let ParagraphChild::Hyperlink(h) = child {
1068                                    if let HyperlinkData::External { rid, path } = &h.link {
1069                                        hyperlink_map.insert(rid.clone(), path.clone());
1070                                    };
1071                                    for child in &h.children {
1072                                        if let ParagraphChild::CommentStart(c) = child {
1073                                            push_comment_and_comment_extended(
1074                                                &mut comments,
1075                                                &mut comments_extended,
1076                                                &comment_map,
1077                                                c,
1078                                            );
1079                                        }
1080                                    }
1081                                }
1082                            }
1083                        }
1084                        if let TocContent::Table(table) = child {
1085                            collect_dependencies_in_table(
1086                                table,
1087                                &mut comments,
1088                                &mut comments_extended,
1089                                &mut comment_map,
1090                                &mut hyperlink_map,
1091                            );
1092                        }
1093                    }
1094                }
1095                _ => {}
1096            }
1097        }
1098        // If this document has comments, set comments.xml to document_rels.
1099        // This is because comments.xml without comment cause an error on word online.
1100        if !comments.is_empty() {
1101            self.document_rels.has_comments = true;
1102        }
1103
1104        self.comments_extended
1105            .add_comments_extended(comments_extended);
1106
1107        self.comments.add_comments(comments);
1108
1109        for (id, d) in hyperlink_map {
1110            self.document_rels
1111                .hyperlinks
1112                .push((id, d, "External".to_string())); // Now support external only
1113        }
1114    }
1115
1116    // Traverse and clone comments from document and add to comments node.
1117    // reader only
1118    pub(crate) fn store_comments(&mut self, comments: &[Comment]) {
1119        for child in &mut self.document.children {
1120            match child {
1121                DocumentChild::Paragraph(paragraph) => {
1122                    for child in &mut paragraph.children {
1123                        if let ParagraphChild::CommentStart(ref mut c) = child {
1124                            let comment_id = c.get_id();
1125                            if let Some(comment) = comments.iter().find(|c| c.id() == comment_id) {
1126                                let comment = comment.clone();
1127                                c.as_mut().comment(comment);
1128                            }
1129                        }
1130                        if let ParagraphChild::Insert(ref mut insert) = child {
1131                            for child in &mut insert.children {
1132                                if let InsertChild::CommentStart(ref mut c) = child {
1133                                    let comment_id = c.get_id();
1134                                    if let Some(comment) =
1135                                        comments.iter().find(|c| c.id() == comment_id)
1136                                    {
1137                                        let comment = comment.clone();
1138                                        c.as_mut().comment(comment);
1139                                    }
1140                                } else if let InsertChild::Delete(ref mut d) = child {
1141                                    for child in &mut d.children {
1142                                        if let DeleteChild::CommentStart(ref mut c) = child {
1143                                            let comment_id = c.get_id();
1144                                            if let Some(comment) =
1145                                                comments.iter().find(|c| c.id() == comment_id)
1146                                            {
1147                                                let comment = comment.clone();
1148                                                c.as_mut().comment(comment);
1149                                            }
1150                                        }
1151                                    }
1152                                }
1153                            }
1154                        }
1155                        if let ParagraphChild::Delete(ref mut delete) = child {
1156                            for child in &mut delete.children {
1157                                if let DeleteChild::CommentStart(ref mut c) = child {
1158                                    let comment_id = c.get_id();
1159                                    if let Some(comment) =
1160                                        comments.iter().find(|c| c.id() == comment_id)
1161                                    {
1162                                        let comment = comment.clone();
1163                                        c.as_mut().comment(comment);
1164                                    }
1165                                }
1166                            }
1167                        }
1168                    }
1169                }
1170                DocumentChild::Table(table) => store_comments_in_table(table, comments),
1171                _ => {}
1172            }
1173        }
1174
1175        if !comments.is_empty() {
1176            self.document_rels.has_comments = true;
1177        }
1178    }
1179
1180    // Traverse and collect images from document.
1181    fn images_in_doc(&mut self) -> (Vec<ImageIdAndPath>, Vec<ImageIdAndBuf>) {
1182        let mut images: Vec<(String, String)> = vec![];
1183        let mut image_bufs: Vec<(String, Vec<u8>)> = vec![];
1184
1185        for child in &mut self.document.children {
1186            match child {
1187                DocumentChild::Paragraph(paragraph) => {
1188                    collect_images_from_paragraph(paragraph, &mut images, &mut image_bufs, None);
1189                }
1190                DocumentChild::Table(table) => {
1191                    collect_images_from_table(table, &mut images, &mut image_bufs, None);
1192                }
1193                _ => {}
1194            }
1195        }
1196        (images, image_bufs)
1197    }
1198
1199    fn images_in_header(&mut self) -> (Vec<Vec<ImageIdAndPath>>, Vec<ImageIdAndBuf>) {
1200        let mut header_images: Vec<Vec<ImageIdAndPath>> = vec![vec![]; 3];
1201        let mut image_bufs: Vec<(String, Vec<u8>)> = vec![];
1202
1203        if let Some((_, header)) = &mut self.document.section_property.header.as_mut() {
1204            let mut images: Vec<ImageIdAndPath> = vec![];
1205            for child in header.children.iter_mut() {
1206                match child {
1207                    HeaderChild::Paragraph(paragraph) => {
1208                        collect_images_from_paragraph(
1209                            paragraph,
1210                            &mut images,
1211                            &mut image_bufs,
1212                            Some("header"),
1213                        );
1214                    }
1215                    HeaderChild::Table(table) => {
1216                        collect_images_from_table(
1217                            table,
1218                            &mut images,
1219                            &mut image_bufs,
1220                            Some("header"),
1221                        );
1222                    }
1223                    HeaderChild::StructuredDataTag(tag) => {
1224                        for child in tag.children.iter_mut() {
1225                            if let StructuredDataTagChild::Paragraph(paragraph) = child {
1226                                collect_images_from_paragraph(
1227                                    paragraph,
1228                                    &mut images,
1229                                    &mut image_bufs,
1230                                    Some("header"),
1231                                );
1232                            }
1233                            if let StructuredDataTagChild::Table(table) = child {
1234                                collect_images_from_table(
1235                                    table,
1236                                    &mut images,
1237                                    &mut image_bufs,
1238                                    Some("header"),
1239                                );
1240                            }
1241                        }
1242                    }
1243                }
1244            }
1245            header_images[0] = images;
1246        }
1247
1248        if let Some((_, header)) = &mut self.document.section_property.first_header.as_mut() {
1249            let mut images: Vec<ImageIdAndPath> = vec![];
1250            for child in header.children.iter_mut() {
1251                match child {
1252                    HeaderChild::Paragraph(paragraph) => {
1253                        collect_images_from_paragraph(
1254                            paragraph,
1255                            &mut images,
1256                            &mut image_bufs,
1257                            Some("header"),
1258                        );
1259                    }
1260                    HeaderChild::Table(table) => {
1261                        collect_images_from_table(
1262                            table,
1263                            &mut images,
1264                            &mut image_bufs,
1265                            Some("header"),
1266                        );
1267                    }
1268                    HeaderChild::StructuredDataTag(tag) => {
1269                        for child in tag.children.iter_mut() {
1270                            if let StructuredDataTagChild::Paragraph(paragraph) = child {
1271                                collect_images_from_paragraph(
1272                                    paragraph,
1273                                    &mut images,
1274                                    &mut image_bufs,
1275                                    Some("header"),
1276                                );
1277                            }
1278                            if let StructuredDataTagChild::Table(table) = child {
1279                                collect_images_from_table(
1280                                    table,
1281                                    &mut images,
1282                                    &mut image_bufs,
1283                                    Some("header"),
1284                                );
1285                            }
1286                        }
1287                    }
1288                }
1289            }
1290            header_images[1] = images;
1291        }
1292
1293        if let Some((_, header)) = &mut self.document.section_property.even_header.as_mut() {
1294            let mut images: Vec<ImageIdAndPath> = vec![];
1295            for child in header.children.iter_mut() {
1296                match child {
1297                    HeaderChild::Paragraph(paragraph) => {
1298                        collect_images_from_paragraph(
1299                            paragraph,
1300                            &mut images,
1301                            &mut image_bufs,
1302                            Some("header"),
1303                        );
1304                    }
1305                    HeaderChild::Table(table) => {
1306                        collect_images_from_table(
1307                            table,
1308                            &mut images,
1309                            &mut image_bufs,
1310                            Some("header"),
1311                        );
1312                    }
1313                    HeaderChild::StructuredDataTag(tag) => {
1314                        for child in tag.children.iter_mut() {
1315                            if let StructuredDataTagChild::Paragraph(paragraph) = child {
1316                                collect_images_from_paragraph(
1317                                    paragraph,
1318                                    &mut images,
1319                                    &mut image_bufs,
1320                                    Some("header"),
1321                                );
1322                            }
1323                            if let StructuredDataTagChild::Table(table) = child {
1324                                collect_images_from_table(
1325                                    table,
1326                                    &mut images,
1327                                    &mut image_bufs,
1328                                    Some("header"),
1329                                );
1330                            }
1331                        }
1332                    }
1333                }
1334            }
1335            header_images[2] = images;
1336        }
1337        (header_images, image_bufs)
1338    }
1339
1340    // Traverse and collect images from header.
1341    fn images_in_footer(&mut self) -> (Vec<Vec<ImageIdAndPath>>, Vec<ImageIdAndBuf>) {
1342        let mut footer_images: Vec<Vec<ImageIdAndPath>> = vec![vec![]; 3];
1343        let mut image_bufs: Vec<(String, Vec<u8>)> = vec![];
1344
1345        if let Some((_, footer)) = &mut self.document.section_property.footer.as_mut() {
1346            let mut images: Vec<ImageIdAndPath> = vec![];
1347            for child in footer.children.iter_mut() {
1348                match child {
1349                    FooterChild::Paragraph(paragraph) => {
1350                        collect_images_from_paragraph(
1351                            paragraph,
1352                            &mut images,
1353                            &mut image_bufs,
1354                            Some("footer"),
1355                        );
1356                    }
1357                    FooterChild::Table(table) => {
1358                        collect_images_from_table(
1359                            table,
1360                            &mut images,
1361                            &mut image_bufs,
1362                            Some("footer"),
1363                        );
1364                    }
1365                    FooterChild::StructuredDataTag(tag) => {
1366                        for child in tag.children.iter_mut() {
1367                            if let StructuredDataTagChild::Paragraph(paragraph) = child {
1368                                collect_images_from_paragraph(
1369                                    paragraph,
1370                                    &mut images,
1371                                    &mut image_bufs,
1372                                    Some("header"),
1373                                );
1374                            }
1375                            if let StructuredDataTagChild::Table(table) = child {
1376                                collect_images_from_table(
1377                                    table,
1378                                    &mut images,
1379                                    &mut image_bufs,
1380                                    Some("header"),
1381                                );
1382                            }
1383                        }
1384                    }
1385                }
1386            }
1387            footer_images[0] = images;
1388        }
1389
1390        if let Some((_, footer)) = &mut self.document.section_property.first_footer.as_mut() {
1391            let mut images: Vec<ImageIdAndPath> = vec![];
1392            for child in footer.children.iter_mut() {
1393                match child {
1394                    FooterChild::Paragraph(paragraph) => {
1395                        collect_images_from_paragraph(
1396                            paragraph,
1397                            &mut images,
1398                            &mut image_bufs,
1399                            Some("footer"),
1400                        );
1401                    }
1402                    FooterChild::Table(table) => {
1403                        collect_images_from_table(
1404                            table,
1405                            &mut images,
1406                            &mut image_bufs,
1407                            Some("footer"),
1408                        );
1409                    }
1410                    FooterChild::StructuredDataTag(tag) => {
1411                        for child in tag.children.iter_mut() {
1412                            if let StructuredDataTagChild::Paragraph(paragraph) = child {
1413                                collect_images_from_paragraph(
1414                                    paragraph,
1415                                    &mut images,
1416                                    &mut image_bufs,
1417                                    Some("header"),
1418                                );
1419                            }
1420                            if let StructuredDataTagChild::Table(table) = child {
1421                                collect_images_from_table(
1422                                    table,
1423                                    &mut images,
1424                                    &mut image_bufs,
1425                                    Some("header"),
1426                                );
1427                            }
1428                        }
1429                    }
1430                }
1431            }
1432            footer_images[1] = images;
1433        }
1434
1435        if let Some((_, footer)) = &mut self.document.section_property.even_footer.as_mut() {
1436            let mut images: Vec<ImageIdAndPath> = vec![];
1437            for child in footer.children.iter_mut() {
1438                match child {
1439                    FooterChild::Paragraph(paragraph) => {
1440                        collect_images_from_paragraph(
1441                            paragraph,
1442                            &mut images,
1443                            &mut image_bufs,
1444                            Some("footer"),
1445                        );
1446                    }
1447                    FooterChild::Table(table) => {
1448                        collect_images_from_table(
1449                            table,
1450                            &mut images,
1451                            &mut image_bufs,
1452                            Some("footer"),
1453                        );
1454                    }
1455                    FooterChild::StructuredDataTag(tag) => {
1456                        for child in tag.children.iter_mut() {
1457                            if let StructuredDataTagChild::Paragraph(paragraph) = child {
1458                                collect_images_from_paragraph(
1459                                    paragraph,
1460                                    &mut images,
1461                                    &mut image_bufs,
1462                                    Some("header"),
1463                                );
1464                            }
1465                            if let StructuredDataTagChild::Table(table) = child {
1466                                collect_images_from_table(
1467                                    table,
1468                                    &mut images,
1469                                    &mut image_bufs,
1470                                    Some("header"),
1471                                );
1472                            }
1473                        }
1474                    }
1475                }
1476            }
1477            footer_images[2] = images;
1478        }
1479        (footer_images, image_bufs)
1480    }
1481
1482    /// Collect footnotes from all Runs to the docx footnotes node.
1483    pub fn collect_footnotes(&mut self) -> bool {
1484        let footnotes: Vec<Footnote> = self
1485            .document
1486            .children
1487            .iter()
1488            .filter_map(|child| match child {
1489                DocumentChild::Paragraph(paragraph) => Some(&paragraph.children),
1490                _ => None,
1491            })
1492            .flat_map(|children| children.iter())
1493            .filter_map(|para_child| match para_child {
1494                ParagraphChild::Run(run) => Some(&run.children),
1495                _ => None,
1496            })
1497            .flat_map(|children| children.iter())
1498            .filter_map(|run_child| match run_child {
1499                RunChild::FootnoteReference(footnote_ref) => Some(footnote_ref),
1500                _ => None,
1501            })
1502            .map(Into::<Footnote>::into)
1503            .collect();
1504        let is_footnotes = !footnotes.is_empty();
1505        self.footnotes.add(footnotes);
1506        is_footnotes
1507    }
1508}
1509
1510fn collect_dependencies_in_paragraph(
1511    paragraph: &Paragraph,
1512    comments: &mut Vec<Comment>,
1513    comments_extended: &mut Vec<CommentExtended>,
1514    comment_map: &mut HashMap<usize, String>,
1515    hyperlink_map: &mut HashMap<String, String>,
1516) {
1517    for child in &paragraph.children {
1518        if let ParagraphChild::CommentStart(c) = child {
1519            push_comment_and_comment_extended(comments, comments_extended, comment_map, c);
1520        }
1521        if let ParagraphChild::Hyperlink(h) = child {
1522            if let HyperlinkData::External { rid, path } = &h.link {
1523                hyperlink_map.insert(rid.clone(), path.clone());
1524            };
1525            for child in &h.children {
1526                if let ParagraphChild::CommentStart(c) = child {
1527                    push_comment_and_comment_extended(comments, comments_extended, comment_map, c);
1528                }
1529            }
1530        }
1531    }
1532}
1533
1534fn collect_comment_map_in_paragraph(
1535    paragraph: &Paragraph,
1536    comment_map: &mut HashMap<usize, String>,
1537) {
1538    for child in &paragraph.children {
1539        if let ParagraphChild::CommentStart(c) = child {
1540            let comment = c.get_comment_ref();
1541            let comment_id = comment.id();
1542            for child in &comment.children {
1543                if let CommentChild::Paragraph(child) = child {
1544                    comment_map.insert(comment_id, child.id.clone());
1545                }
1546            }
1547        }
1548        if let ParagraphChild::Hyperlink(h) = child {
1549            for child in &h.children {
1550                if let ParagraphChild::CommentStart(c) = child {
1551                    let comment = c.get_comment_ref();
1552                    let comment_id = comment.id();
1553                    for child in &comment.children {
1554                        if let CommentChild::Paragraph(child) = child {
1555                            comment_map.insert(comment_id, child.id.clone());
1556                        }
1557                    }
1558                }
1559            }
1560        }
1561    }
1562}
1563
1564fn collect_comment_map_in_table(table: &Table, comment_map: &mut HashMap<usize, String>) {
1565    for TableChild::TableRow(row) in &table.rows {
1566        for TableRowChild::TableCell(cell) in &row.cells {
1567            for content in &cell.children {
1568                match content {
1569                    TableCellContent::Paragraph(paragraph) => {
1570                        collect_comment_map_in_paragraph(paragraph, comment_map);
1571                    }
1572                    TableCellContent::Table(table) => {
1573                        collect_comment_map_in_table(table, comment_map)
1574                    }
1575                    TableCellContent::StructuredDataTag(tag) => {
1576                        for child in &tag.children {
1577                            if let StructuredDataTagChild::Paragraph(paragraph) = child {
1578                                collect_comment_map_in_paragraph(paragraph, comment_map);
1579                            }
1580                            if let StructuredDataTagChild::Table(table) = child {
1581                                collect_comment_map_in_table(table, comment_map);
1582                            }
1583                        }
1584                    }
1585                    TableCellContent::TableOfContents(t) => {
1586                        for child in &t.before_contents {
1587                            if let TocContent::Paragraph(paragraph) = child {
1588                                collect_comment_map_in_paragraph(paragraph, comment_map);
1589                            }
1590                            if let TocContent::Table(table) = child {
1591                                collect_comment_map_in_table(table, comment_map);
1592                            }
1593                        }
1594                        for child in &t.after_contents {
1595                            if let TocContent::Paragraph(paragraph) = child {
1596                                collect_comment_map_in_paragraph(paragraph, comment_map);
1597                            }
1598                            if let TocContent::Table(table) = child {
1599                                collect_comment_map_in_table(table, comment_map);
1600                            }
1601                        }
1602                    }
1603                }
1604            }
1605        }
1606    }
1607}
1608
1609fn collect_dependencies_in_table(
1610    table: &Table,
1611    comments: &mut Vec<Comment>,
1612    comments_extended: &mut Vec<CommentExtended>,
1613    comment_map: &mut HashMap<usize, String>,
1614    hyperlink_map: &mut HashMap<String, String>,
1615) {
1616    for TableChild::TableRow(row) in &table.rows {
1617        for TableRowChild::TableCell(cell) in &row.cells {
1618            for content in &cell.children {
1619                match content {
1620                    TableCellContent::Paragraph(paragraph) => {
1621                        collect_dependencies_in_paragraph(
1622                            paragraph,
1623                            comments,
1624                            comments_extended,
1625                            comment_map,
1626                            hyperlink_map,
1627                        );
1628                    }
1629                    TableCellContent::Table(table) => collect_dependencies_in_table(
1630                        table,
1631                        comments,
1632                        comments_extended,
1633                        comment_map,
1634                        hyperlink_map,
1635                    ),
1636                    TableCellContent::StructuredDataTag(tag) => {
1637                        for child in &tag.children {
1638                            if let StructuredDataTagChild::Paragraph(paragraph) = child {
1639                                collect_dependencies_in_paragraph(
1640                                    paragraph,
1641                                    comments,
1642                                    comments_extended,
1643                                    comment_map,
1644                                    hyperlink_map,
1645                                );
1646                            }
1647                            if let StructuredDataTagChild::Table(table) = child {
1648                                collect_dependencies_in_table(
1649                                    table,
1650                                    comments,
1651                                    comments_extended,
1652                                    comment_map,
1653                                    hyperlink_map,
1654                                );
1655                            }
1656                        }
1657                    }
1658                    TableCellContent::TableOfContents(t) => {
1659                        for child in &t.before_contents {
1660                            if let TocContent::Paragraph(paragraph) = child {
1661                                collect_dependencies_in_paragraph(
1662                                    paragraph,
1663                                    comments,
1664                                    comments_extended,
1665                                    comment_map,
1666                                    hyperlink_map,
1667                                );
1668                            }
1669                            if let TocContent::Table(table) = child {
1670                                collect_dependencies_in_table(
1671                                    table,
1672                                    comments,
1673                                    comments_extended,
1674                                    comment_map,
1675                                    hyperlink_map,
1676                                );
1677                            }
1678                        }
1679
1680                        for child in &t.after_contents {
1681                            if let TocContent::Paragraph(paragraph) = child {
1682                                collect_dependencies_in_paragraph(
1683                                    paragraph,
1684                                    comments,
1685                                    comments_extended,
1686                                    comment_map,
1687                                    hyperlink_map,
1688                                );
1689                            }
1690                            if let TocContent::Table(table) = child {
1691                                collect_dependencies_in_table(
1692                                    table,
1693                                    comments,
1694                                    comments_extended,
1695                                    comment_map,
1696                                    hyperlink_map,
1697                                );
1698                            }
1699                        }
1700                    }
1701                }
1702            }
1703        }
1704    }
1705}
1706
1707fn store_comments_in_paragraph(paragraph: &mut Paragraph, comments: &[Comment]) {
1708    for child in &mut paragraph.children {
1709        if let ParagraphChild::CommentStart(ref mut c) = child {
1710            let comment_id = c.get_id();
1711            if let Some(comment) = comments.iter().find(|c| c.id() == comment_id) {
1712                let comment = comment.clone();
1713                c.as_mut().comment(comment);
1714            }
1715        }
1716        if let ParagraphChild::Insert(ref mut insert) = child {
1717            for child in &mut insert.children {
1718                if let InsertChild::CommentStart(ref mut c) = child {
1719                    let comment_id = c.get_id();
1720                    if let Some(comment) = comments.iter().find(|c| c.id() == comment_id) {
1721                        let comment = comment.clone();
1722                        c.as_mut().comment(comment);
1723                    }
1724                }
1725            }
1726        }
1727        if let ParagraphChild::Delete(ref mut delete) = child {
1728            for child in &mut delete.children {
1729                if let DeleteChild::CommentStart(ref mut c) = child {
1730                    let comment_id = c.get_id();
1731                    if let Some(comment) = comments.iter().find(|c| c.id() == comment_id) {
1732                        let comment = comment.clone();
1733                        c.as_mut().comment(comment);
1734                    }
1735                }
1736            }
1737        }
1738    }
1739}
1740
1741fn store_comments_in_table(table: &mut Table, comments: &[Comment]) {
1742    for TableChild::TableRow(row) in &mut table.rows {
1743        for TableRowChild::TableCell(cell) in &mut row.cells {
1744            for content in &mut cell.children {
1745                match content {
1746                    TableCellContent::Paragraph(paragraph) => {
1747                        store_comments_in_paragraph(paragraph, comments)
1748                    }
1749                    TableCellContent::Table(ref mut table) => {
1750                        store_comments_in_table(table, comments);
1751                    }
1752                    TableCellContent::StructuredDataTag(ref mut tag) => {
1753                        for child in &mut tag.children {
1754                            if let StructuredDataTagChild::Paragraph(paragraph) = child {
1755                                store_comments_in_paragraph(paragraph, comments);
1756                            }
1757                            if let StructuredDataTagChild::Table(table) = child {
1758                                store_comments_in_table(table, comments);
1759                            }
1760                        }
1761                    }
1762                    TableCellContent::TableOfContents(ref mut t) => {
1763                        for child in &mut t.before_contents {
1764                            if let TocContent::Paragraph(paragraph) = child {
1765                                store_comments_in_paragraph(paragraph, comments);
1766                            }
1767                            if let TocContent::Table(table) = child {
1768                                store_comments_in_table(table, comments);
1769                            }
1770                        }
1771
1772                        for child in &mut t.after_contents {
1773                            if let TocContent::Paragraph(paragraph) = child {
1774                                store_comments_in_paragraph(paragraph, comments);
1775                            }
1776                            if let TocContent::Table(table) = child {
1777                                store_comments_in_table(table, comments);
1778                            }
1779                        }
1780                    }
1781                }
1782            }
1783        }
1784    }
1785}
1786
1787fn collect_para_ids_in_docx(docx: &Docx, counts: &mut HashMap<String, usize>) {
1788    for child in &docx.document.children {
1789        collect_para_ids_in_document_child(child, counts);
1790    }
1791    collect_para_ids_in_section_property(&docx.document.section_property, counts);
1792
1793    for comment in &docx.comments.comments {
1794        collect_para_ids_in_comment(comment, counts);
1795    }
1796
1797    for footnote in &docx.footnotes.footnotes {
1798        for paragraph in &footnote.content {
1799            collect_para_ids_in_paragraph(paragraph, counts);
1800        }
1801    }
1802}
1803
1804fn collect_para_ids_in_document_child(child: &DocumentChild, counts: &mut HashMap<String, usize>) {
1805    match child {
1806        DocumentChild::Paragraph(paragraph) => collect_para_ids_in_paragraph(paragraph, counts),
1807        DocumentChild::Table(table) => collect_para_ids_in_table(table, counts),
1808        DocumentChild::StructuredDataTag(tag) => {
1809            collect_para_ids_in_structured_data_tag(tag, counts)
1810        }
1811        DocumentChild::TableOfContents(toc) => collect_para_ids_in_toc(toc, counts),
1812        DocumentChild::Section(section) => collect_para_ids_in_section(section, counts),
1813        _ => {}
1814    }
1815}
1816
1817fn collect_para_ids_in_section(section: &Section, counts: &mut HashMap<String, usize>) {
1818    for child in &section.children {
1819        match child {
1820            SectionChild::Paragraph(paragraph) => collect_para_ids_in_paragraph(paragraph, counts),
1821            SectionChild::Table(table) => collect_para_ids_in_table(table, counts),
1822            SectionChild::StructuredDataTag(tag) => {
1823                collect_para_ids_in_structured_data_tag(tag, counts)
1824            }
1825            SectionChild::TableOfContents(toc) => collect_para_ids_in_toc(toc, counts),
1826            _ => {}
1827        }
1828    }
1829    collect_para_ids_in_section_property(&section.property, counts);
1830}
1831
1832fn collect_para_ids_in_section_property(
1833    property: &SectionProperty,
1834    counts: &mut HashMap<String, usize>,
1835) {
1836    if let Some((_, header)) = property.header.as_ref() {
1837        collect_para_ids_in_header(header, counts);
1838    }
1839    if let Some((_, header)) = property.first_header.as_ref() {
1840        collect_para_ids_in_header(header, counts);
1841    }
1842    if let Some((_, header)) = property.even_header.as_ref() {
1843        collect_para_ids_in_header(header, counts);
1844    }
1845    if let Some((_, footer)) = property.footer.as_ref() {
1846        collect_para_ids_in_footer(footer, counts);
1847    }
1848    if let Some((_, footer)) = property.first_footer.as_ref() {
1849        collect_para_ids_in_footer(footer, counts);
1850    }
1851    if let Some((_, footer)) = property.even_footer.as_ref() {
1852        collect_para_ids_in_footer(footer, counts);
1853    }
1854}
1855
1856fn collect_para_ids_in_header(header: &Header, counts: &mut HashMap<String, usize>) {
1857    for child in &header.children {
1858        match child {
1859            HeaderChild::Paragraph(paragraph) => collect_para_ids_in_paragraph(paragraph, counts),
1860            HeaderChild::Table(table) => collect_para_ids_in_table(table, counts),
1861            HeaderChild::StructuredDataTag(tag) => {
1862                collect_para_ids_in_structured_data_tag(tag, counts)
1863            }
1864        }
1865    }
1866}
1867
1868fn collect_para_ids_in_footer(footer: &Footer, counts: &mut HashMap<String, usize>) {
1869    for child in &footer.children {
1870        match child {
1871            FooterChild::Paragraph(paragraph) => collect_para_ids_in_paragraph(paragraph, counts),
1872            FooterChild::Table(table) => collect_para_ids_in_table(table, counts),
1873            FooterChild::StructuredDataTag(tag) => {
1874                collect_para_ids_in_structured_data_tag(tag, counts)
1875            }
1876        }
1877    }
1878}
1879
1880fn collect_para_ids_in_toc(toc: &TableOfContents, counts: &mut HashMap<String, usize>) {
1881    for child in &toc.before_contents {
1882        match child {
1883            TocContent::Paragraph(paragraph) => collect_para_ids_in_paragraph(paragraph, counts),
1884            TocContent::Table(table) => collect_para_ids_in_table(table, counts),
1885        }
1886    }
1887    for child in &toc.after_contents {
1888        match child {
1889            TocContent::Paragraph(paragraph) => collect_para_ids_in_paragraph(paragraph, counts),
1890            TocContent::Table(table) => collect_para_ids_in_table(table, counts),
1891        }
1892    }
1893}
1894
1895fn collect_para_ids_in_table(table: &Table, counts: &mut HashMap<String, usize>) {
1896    for TableChild::TableRow(row) in &table.rows {
1897        for TableRowChild::TableCell(cell) in &row.cells {
1898            for content in &cell.children {
1899                match content {
1900                    TableCellContent::Paragraph(paragraph) => {
1901                        collect_para_ids_in_paragraph(paragraph, counts)
1902                    }
1903                    TableCellContent::Table(table) => collect_para_ids_in_table(table, counts),
1904                    TableCellContent::StructuredDataTag(tag) => {
1905                        collect_para_ids_in_structured_data_tag(tag, counts)
1906                    }
1907                    TableCellContent::TableOfContents(toc) => collect_para_ids_in_toc(toc, counts),
1908                }
1909            }
1910        }
1911    }
1912}
1913
1914fn collect_para_ids_in_paragraph(paragraph: &Paragraph, counts: &mut HashMap<String, usize>) {
1915    *counts.entry(paragraph.id.clone()).or_insert(0) += 1;
1916
1917    for child in &paragraph.children {
1918        match child {
1919            ParagraphChild::CommentStart(c) => collect_para_ids_in_comment(&c.comment, counts),
1920            ParagraphChild::Insert(insert) => collect_para_ids_in_insert(insert, counts),
1921            ParagraphChild::Delete(delete) => collect_para_ids_in_delete(delete, counts),
1922            ParagraphChild::Hyperlink(hyperlink) => {
1923                collect_para_ids_in_hyperlink(hyperlink, counts)
1924            }
1925            ParagraphChild::StructuredDataTag(tag) => {
1926                collect_para_ids_in_structured_data_tag(tag, counts)
1927            }
1928            _ => {}
1929        }
1930    }
1931}
1932
1933fn collect_para_ids_in_hyperlink(hyperlink: &Hyperlink, counts: &mut HashMap<String, usize>) {
1934    for child in &hyperlink.children {
1935        match child {
1936            ParagraphChild::CommentStart(c) => collect_para_ids_in_comment(&c.comment, counts),
1937            ParagraphChild::Insert(insert) => collect_para_ids_in_insert(insert, counts),
1938            ParagraphChild::Delete(delete) => collect_para_ids_in_delete(delete, counts),
1939            ParagraphChild::StructuredDataTag(tag) => {
1940                collect_para_ids_in_structured_data_tag(tag, counts)
1941            }
1942            _ => {}
1943        }
1944    }
1945}
1946
1947fn collect_para_ids_in_insert(insert: &Insert, counts: &mut HashMap<String, usize>) {
1948    for child in &insert.children {
1949        match child {
1950            InsertChild::CommentStart(c) => collect_para_ids_in_comment(&c.comment, counts),
1951            InsertChild::Delete(delete) => collect_para_ids_in_delete(delete, counts),
1952            _ => {}
1953        }
1954    }
1955}
1956
1957fn collect_para_ids_in_delete(delete: &Delete, counts: &mut HashMap<String, usize>) {
1958    for child in &delete.children {
1959        if let DeleteChild::CommentStart(c) = child {
1960            collect_para_ids_in_comment(&c.comment, counts);
1961        }
1962    }
1963}
1964
1965fn collect_para_ids_in_structured_data_tag(
1966    tag: &StructuredDataTag,
1967    counts: &mut HashMap<String, usize>,
1968) {
1969    for child in &tag.children {
1970        match child {
1971            StructuredDataTagChild::Paragraph(paragraph) => {
1972                collect_para_ids_in_paragraph(paragraph, counts)
1973            }
1974            StructuredDataTagChild::Table(table) => collect_para_ids_in_table(table, counts),
1975            StructuredDataTagChild::CommentStart(c) => {
1976                collect_para_ids_in_comment(&c.comment, counts)
1977            }
1978            StructuredDataTagChild::StructuredDataTag(inner) => {
1979                collect_para_ids_in_structured_data_tag(inner, counts)
1980            }
1981            _ => {}
1982        }
1983    }
1984}
1985
1986fn collect_para_ids_in_comment(comment: &Comment, counts: &mut HashMap<String, usize>) {
1987    for child in &comment.children {
1988        match child {
1989            CommentChild::Paragraph(paragraph) => collect_para_ids_in_paragraph(paragraph, counts),
1990            CommentChild::Table(table) => collect_para_ids_in_table(table, counts),
1991        }
1992    }
1993}
1994
1995fn refresh_para_ids_in_docx(
1996    docx: &mut Docx,
1997    counts: &HashMap<String, usize>,
1998    used: &mut HashSet<String>,
1999    seen: &mut HashSet<String>,
2000) {
2001    for child in &mut docx.document.children {
2002        refresh_para_ids_in_document_child(child, counts, used, seen);
2003    }
2004    refresh_para_ids_in_section_property(&mut docx.document.section_property, counts, used, seen);
2005
2006    for comment in &mut docx.comments.comments {
2007        refresh_para_ids_in_comment(comment, counts, used, seen);
2008    }
2009
2010    for footnote in &mut docx.footnotes.footnotes {
2011        for paragraph in &mut footnote.content {
2012            refresh_para_ids_in_paragraph(paragraph, counts, used, seen);
2013        }
2014    }
2015}
2016
2017fn refresh_para_ids_in_document_child(
2018    child: &mut DocumentChild,
2019    counts: &HashMap<String, usize>,
2020    used: &mut HashSet<String>,
2021    seen: &mut HashSet<String>,
2022) {
2023    match child {
2024        DocumentChild::Paragraph(paragraph) => {
2025            refresh_para_ids_in_paragraph(paragraph, counts, used, seen)
2026        }
2027        DocumentChild::Table(table) => refresh_para_ids_in_table(table, counts, used, seen),
2028        DocumentChild::StructuredDataTag(tag) => {
2029            refresh_para_ids_in_structured_data_tag(tag, counts, used, seen)
2030        }
2031        DocumentChild::TableOfContents(toc) => refresh_para_ids_in_toc(toc, counts, used, seen),
2032        DocumentChild::Section(section) => refresh_para_ids_in_section(section, counts, used, seen),
2033        _ => {}
2034    }
2035}
2036
2037fn refresh_para_ids_in_section(
2038    section: &mut Section,
2039    counts: &HashMap<String, usize>,
2040    used: &mut HashSet<String>,
2041    seen: &mut HashSet<String>,
2042) {
2043    for child in &mut section.children {
2044        match child {
2045            SectionChild::Paragraph(paragraph) => {
2046                refresh_para_ids_in_paragraph(paragraph, counts, used, seen)
2047            }
2048            SectionChild::Table(table) => refresh_para_ids_in_table(table, counts, used, seen),
2049            SectionChild::StructuredDataTag(tag) => {
2050                refresh_para_ids_in_structured_data_tag(tag, counts, used, seen)
2051            }
2052            SectionChild::TableOfContents(toc) => refresh_para_ids_in_toc(toc, counts, used, seen),
2053            _ => {}
2054        }
2055    }
2056    refresh_para_ids_in_section_property(&mut section.property, counts, used, seen);
2057}
2058
2059fn refresh_para_ids_in_section_property(
2060    property: &mut SectionProperty,
2061    counts: &HashMap<String, usize>,
2062    used: &mut HashSet<String>,
2063    seen: &mut HashSet<String>,
2064) {
2065    if let Some((_, header)) = property.header.as_mut() {
2066        refresh_para_ids_in_header(header, counts, used, seen);
2067    }
2068    if let Some((_, header)) = property.first_header.as_mut() {
2069        refresh_para_ids_in_header(header, counts, used, seen);
2070    }
2071    if let Some((_, header)) = property.even_header.as_mut() {
2072        refresh_para_ids_in_header(header, counts, used, seen);
2073    }
2074    if let Some((_, footer)) = property.footer.as_mut() {
2075        refresh_para_ids_in_footer(footer, counts, used, seen);
2076    }
2077    if let Some((_, footer)) = property.first_footer.as_mut() {
2078        refresh_para_ids_in_footer(footer, counts, used, seen);
2079    }
2080    if let Some((_, footer)) = property.even_footer.as_mut() {
2081        refresh_para_ids_in_footer(footer, counts, used, seen);
2082    }
2083}
2084
2085fn refresh_para_ids_in_header(
2086    header: &mut Header,
2087    counts: &HashMap<String, usize>,
2088    used: &mut HashSet<String>,
2089    seen: &mut HashSet<String>,
2090) {
2091    for child in &mut header.children {
2092        match child {
2093            HeaderChild::Paragraph(paragraph) => {
2094                refresh_para_ids_in_paragraph(paragraph, counts, used, seen)
2095            }
2096            HeaderChild::Table(table) => refresh_para_ids_in_table(table, counts, used, seen),
2097            HeaderChild::StructuredDataTag(tag) => {
2098                refresh_para_ids_in_structured_data_tag(tag, counts, used, seen)
2099            }
2100        }
2101    }
2102}
2103
2104fn refresh_para_ids_in_footer(
2105    footer: &mut Footer,
2106    counts: &HashMap<String, usize>,
2107    used: &mut HashSet<String>,
2108    seen: &mut HashSet<String>,
2109) {
2110    for child in &mut footer.children {
2111        match child {
2112            FooterChild::Paragraph(paragraph) => {
2113                refresh_para_ids_in_paragraph(paragraph, counts, used, seen)
2114            }
2115            FooterChild::Table(table) => refresh_para_ids_in_table(table, counts, used, seen),
2116            FooterChild::StructuredDataTag(tag) => {
2117                refresh_para_ids_in_structured_data_tag(tag, counts, used, seen)
2118            }
2119        }
2120    }
2121}
2122
2123fn refresh_para_ids_in_toc(
2124    toc: &mut TableOfContents,
2125    counts: &HashMap<String, usize>,
2126    used: &mut HashSet<String>,
2127    seen: &mut HashSet<String>,
2128) {
2129    for child in &mut toc.before_contents {
2130        match child {
2131            TocContent::Paragraph(paragraph) => {
2132                refresh_para_ids_in_paragraph(paragraph, counts, used, seen)
2133            }
2134            TocContent::Table(table) => refresh_para_ids_in_table(table, counts, used, seen),
2135        }
2136    }
2137    for child in &mut toc.after_contents {
2138        match child {
2139            TocContent::Paragraph(paragraph) => {
2140                refresh_para_ids_in_paragraph(paragraph, counts, used, seen)
2141            }
2142            TocContent::Table(table) => refresh_para_ids_in_table(table, counts, used, seen),
2143        }
2144    }
2145}
2146
2147fn refresh_para_ids_in_table(
2148    table: &mut Table,
2149    counts: &HashMap<String, usize>,
2150    used: &mut HashSet<String>,
2151    seen: &mut HashSet<String>,
2152) {
2153    for TableChild::TableRow(row) in &mut table.rows {
2154        for TableRowChild::TableCell(cell) in &mut row.cells {
2155            for content in &mut cell.children {
2156                match content {
2157                    TableCellContent::Paragraph(paragraph) => {
2158                        refresh_para_ids_in_paragraph(paragraph, counts, used, seen)
2159                    }
2160                    TableCellContent::Table(table) => {
2161                        refresh_para_ids_in_table(table, counts, used, seen)
2162                    }
2163                    TableCellContent::StructuredDataTag(tag) => {
2164                        refresh_para_ids_in_structured_data_tag(tag, counts, used, seen)
2165                    }
2166                    TableCellContent::TableOfContents(toc) => {
2167                        refresh_para_ids_in_toc(toc, counts, used, seen)
2168                    }
2169                }
2170            }
2171        }
2172    }
2173}
2174
2175fn refresh_para_ids_in_paragraph(
2176    paragraph: &mut Paragraph,
2177    counts: &HashMap<String, usize>,
2178    used: &mut HashSet<String>,
2179    seen: &mut HashSet<String>,
2180) {
2181    ensure_unique_paragraph_id(paragraph, counts, used, seen);
2182
2183    for child in &mut paragraph.children {
2184        match child {
2185            ParagraphChild::CommentStart(c) => {
2186                refresh_para_ids_in_comment(&mut c.comment, counts, used, seen)
2187            }
2188            ParagraphChild::Insert(insert) => {
2189                refresh_para_ids_in_insert(insert, counts, used, seen)
2190            }
2191            ParagraphChild::Delete(delete) => {
2192                refresh_para_ids_in_delete(delete, counts, used, seen)
2193            }
2194            ParagraphChild::Hyperlink(hyperlink) => {
2195                refresh_para_ids_in_hyperlink(hyperlink, counts, used, seen)
2196            }
2197            ParagraphChild::StructuredDataTag(tag) => {
2198                refresh_para_ids_in_structured_data_tag(tag, counts, used, seen)
2199            }
2200            _ => {}
2201        }
2202    }
2203}
2204
2205fn refresh_para_ids_in_hyperlink(
2206    hyperlink: &mut Hyperlink,
2207    counts: &HashMap<String, usize>,
2208    used: &mut HashSet<String>,
2209    seen: &mut HashSet<String>,
2210) {
2211    for child in &mut hyperlink.children {
2212        match child {
2213            ParagraphChild::CommentStart(c) => {
2214                refresh_para_ids_in_comment(&mut c.comment, counts, used, seen)
2215            }
2216            ParagraphChild::Insert(insert) => {
2217                refresh_para_ids_in_insert(insert, counts, used, seen)
2218            }
2219            ParagraphChild::Delete(delete) => {
2220                refresh_para_ids_in_delete(delete, counts, used, seen)
2221            }
2222            ParagraphChild::StructuredDataTag(tag) => {
2223                refresh_para_ids_in_structured_data_tag(tag, counts, used, seen)
2224            }
2225            _ => {}
2226        }
2227    }
2228}
2229
2230fn refresh_para_ids_in_insert(
2231    insert: &mut Insert,
2232    counts: &HashMap<String, usize>,
2233    used: &mut HashSet<String>,
2234    seen: &mut HashSet<String>,
2235) {
2236    for child in &mut insert.children {
2237        match child {
2238            InsertChild::CommentStart(c) => {
2239                refresh_para_ids_in_comment(&mut c.comment, counts, used, seen)
2240            }
2241            InsertChild::Delete(delete) => refresh_para_ids_in_delete(delete, counts, used, seen),
2242            _ => {}
2243        }
2244    }
2245}
2246
2247fn refresh_para_ids_in_delete(
2248    delete: &mut Delete,
2249    counts: &HashMap<String, usize>,
2250    used: &mut HashSet<String>,
2251    seen: &mut HashSet<String>,
2252) {
2253    for child in &mut delete.children {
2254        if let DeleteChild::CommentStart(c) = child {
2255            refresh_para_ids_in_comment(&mut c.comment, counts, used, seen);
2256        }
2257    }
2258}
2259
2260fn refresh_para_ids_in_structured_data_tag(
2261    tag: &mut StructuredDataTag,
2262    counts: &HashMap<String, usize>,
2263    used: &mut HashSet<String>,
2264    seen: &mut HashSet<String>,
2265) {
2266    for child in &mut tag.children {
2267        match child {
2268            StructuredDataTagChild::Paragraph(paragraph) => {
2269                refresh_para_ids_in_paragraph(paragraph, counts, used, seen)
2270            }
2271            StructuredDataTagChild::Table(table) => {
2272                refresh_para_ids_in_table(table, counts, used, seen)
2273            }
2274            StructuredDataTagChild::CommentStart(c) => {
2275                refresh_para_ids_in_comment(&mut c.comment, counts, used, seen)
2276            }
2277            StructuredDataTagChild::StructuredDataTag(inner) => {
2278                refresh_para_ids_in_structured_data_tag(inner, counts, used, seen)
2279            }
2280            _ => {}
2281        }
2282    }
2283}
2284
2285fn refresh_para_ids_in_comment(
2286    comment: &mut Comment,
2287    counts: &HashMap<String, usize>,
2288    used: &mut HashSet<String>,
2289    seen: &mut HashSet<String>,
2290) {
2291    for child in &mut comment.children {
2292        match child {
2293            CommentChild::Paragraph(paragraph) => {
2294                refresh_para_ids_in_paragraph(paragraph, counts, used, seen)
2295            }
2296            CommentChild::Table(table) => refresh_para_ids_in_table(table, counts, used, seen),
2297        }
2298    }
2299}
2300
2301fn ensure_unique_paragraph_id(
2302    paragraph: &mut Paragraph,
2303    counts: &HashMap<String, usize>,
2304    used: &mut HashSet<String>,
2305    seen: &mut HashSet<String>,
2306) {
2307    let id = paragraph.id.clone();
2308    let count = counts.get(&id).copied().unwrap_or(0);
2309
2310    if !id.is_empty() && count <= 1 {
2311        return;
2312    }
2313    if !id.is_empty() && count > 1 && !seen.contains(&id) {
2314        seen.insert(id);
2315        return;
2316    }
2317
2318    let mut new_id = crate::generate_para_id();
2319    while used.contains(&new_id) {
2320        new_id = crate::generate_para_id();
2321    }
2322    paragraph.id = new_id.clone();
2323    used.insert(new_id);
2324}
2325
2326fn push_comment_and_comment_extended(
2327    comments: &mut Vec<Comment>,
2328    comments_extended: &mut Vec<CommentExtended>,
2329    comment_map: &HashMap<usize, String>,
2330    c: &CommentRangeStart,
2331) {
2332    let comment = c.get_comment_ref();
2333    for child in &comment.children {
2334        if let CommentChild::Paragraph(child) = child {
2335            let para_id = child.id.clone();
2336            comments.push(comment.clone());
2337            let comment_extended = CommentExtended::new(para_id);
2338            if let Some(parent_comment_id) = comment.parent_comment_id {
2339                if let Some(parent_para_id) = comment_map.get(&parent_comment_id) {
2340                    comments_extended
2341                        .push(comment_extended.parent_paragraph_id(parent_para_id.clone()));
2342                }
2343            } else {
2344                comments_extended.push(comment_extended);
2345            }
2346        }
2347        // TODO: Support table in comment
2348    }
2349}
2350
2351fn update_document_by_toc(
2352    document_children: Vec<DocumentChild>,
2353    styles: &Styles,
2354    toc: TableOfContents,
2355    toc_index: usize,
2356) -> Vec<DocumentChild> {
2357    let heading_map = styles.create_heading_style_map();
2358    let mut items = vec![];
2359    let mut children = vec![];
2360    let style_map: std::collections::HashMap<String, usize> = toc
2361        .instr
2362        .styles_with_levels
2363        .iter()
2364        .map(|sl| sl.0.clone())
2365        .collect();
2366
2367    if toc.instr.heading_styles_range.is_none() && !toc.instr.styles_with_levels.is_empty() {
2368        // INFO: if \t option set without heading styles ranges, Microsoft word does not show ToC items...
2369        return document_children;
2370    }
2371
2372    let (min, max) = toc.instr.heading_styles_range.unwrap_or((0, 9));
2373
2374    for child in document_children.into_iter() {
2375        match child {
2376            DocumentChild::Paragraph(mut paragraph) => {
2377                if let Some(heading_level) = paragraph
2378                    .property
2379                    .style
2380                    .as_ref()
2381                    .map(|p| p.val.to_string())
2382                    .and_then(|sid| heading_map.get(&sid))
2383                {
2384                    if min <= *heading_level && max >= *heading_level {
2385                        let toc_key = TocKey::generate();
2386                        items.push(
2387                            TableOfContentsItem::new()
2388                                .text(paragraph.raw_text())
2389                                .toc_key(&toc_key)
2390                                .level(*heading_level),
2391                        );
2392                        paragraph =
2393                            Box::new(paragraph.wrap_by_bookmark(generate_bookmark_id(), &toc_key));
2394                    }
2395
2396                    if let Some((_min, _max)) = toc.instr.tc_field_level_range {
2397                        // TODO: check tc field
2398                    }
2399                }
2400
2401                // Support \t option. Collect toc items if style id matched.
2402                if let Some(level) = paragraph
2403                    .property
2404                    .style
2405                    .as_ref()
2406                    .and_then(|s| style_map.get(&s.val))
2407                {
2408                    if min <= *level && max >= *level {
2409                        let toc_key = TocKey::generate();
2410                        items.push(
2411                            TableOfContentsItem::new()
2412                                .text(paragraph.raw_text())
2413                                .toc_key(&toc_key)
2414                                .level(*level),
2415                        );
2416                        paragraph =
2417                            Box::new(paragraph.wrap_by_bookmark(generate_bookmark_id(), &toc_key));
2418                    }
2419                }
2420
2421                children.push(DocumentChild::Paragraph(paragraph));
2422            }
2423            DocumentChild::Table(ref _table) => {
2424                // TODO:
2425                // for row in &table.rows {
2426                //     for cell in &row.cells {
2427                //         for content in &cell.children {
2428                //             match content {
2429                //                 TableCellContent::Paragraph(paragraph) => {}
2430                //                 TableCellContent::Table(_) => {
2431                //                     // TODO: Support table in table
2432                //                 }
2433                //             }
2434                //         }
2435                //     }
2436                // }
2437                children.push(child);
2438            }
2439            _ => {
2440                children.push(child);
2441            }
2442        }
2443    }
2444
2445    let mut toc = toc;
2446    toc.items = items;
2447    children[toc_index] = DocumentChild::TableOfContents(Box::new(toc));
2448    children
2449}
2450
2451#[cfg(test)]
2452mod emf_tests {
2453    use super::*;
2454
2455    /// Build a syntactically-valid, minimal EMF: an EMR_HEADER followed by
2456    /// an EMR_EOF.
2457    pub(super) fn minimal_valid_emf() -> Vec<u8> {
2458        let mut buf = Vec::<u8>::with_capacity(108);
2459
2460        // ---- EMR_HEADER (88 bytes) ----
2461        buf.extend_from_slice(&1u32.to_le_bytes()); // record type
2462        buf.extend_from_slice(&88u32.to_le_bytes()); // record size
2463                                                     // Bounds rect (RECTL)
2464        buf.extend_from_slice(&0i32.to_le_bytes());
2465        buf.extend_from_slice(&0i32.to_le_bytes());
2466        buf.extend_from_slice(&100i32.to_le_bytes());
2467        buf.extend_from_slice(&100i32.to_le_bytes());
2468        // Frame rect (RECTL)
2469        buf.extend_from_slice(&0i32.to_le_bytes());
2470        buf.extend_from_slice(&0i32.to_le_bytes());
2471        buf.extend_from_slice(&2540i32.to_le_bytes());
2472        buf.extend_from_slice(&2540i32.to_le_bytes());
2473        // " EMF" signature, version, total bytes, record count
2474        buf.extend_from_slice(&0x464D_4520u32.to_le_bytes());
2475        buf.extend_from_slice(&0x0001_0000u32.to_le_bytes());
2476        buf.extend_from_slice(&108u32.to_le_bytes());
2477        buf.extend_from_slice(&2u32.to_le_bytes());
2478        // handles, reserved, description, palette
2479        buf.extend_from_slice(&0u16.to_le_bytes());
2480        buf.extend_from_slice(&0u16.to_le_bytes());
2481        buf.extend_from_slice(&0u32.to_le_bytes());
2482        buf.extend_from_slice(&0u32.to_le_bytes());
2483        buf.extend_from_slice(&0u32.to_le_bytes());
2484        // Device size (SIZEL)
2485        buf.extend_from_slice(&1024i32.to_le_bytes());
2486        buf.extend_from_slice(&768i32.to_le_bytes());
2487        // Millimeters size (SIZEL)
2488        buf.extend_from_slice(&320i32.to_le_bytes());
2489        buf.extend_from_slice(&240i32.to_le_bytes());
2490        debug_assert_eq!(buf.len(), 88);
2491
2492        // ---- EMR_EOF (20 bytes) ----
2493        buf.extend_from_slice(&14u32.to_le_bytes()); // record type
2494        buf.extend_from_slice(&20u32.to_le_bytes()); // record size
2495        buf.extend_from_slice(&0u32.to_le_bytes()); // nPalEntries
2496        buf.extend_from_slice(&0u32.to_le_bytes()); // offPalEntries
2497        buf.extend_from_slice(&20u32.to_le_bytes()); // SizeLast
2498
2499        buf
2500    }
2501
2502    /// 44-byte buffer with the right magic bytes but no payload. Detected
2503    /// as EMF, but can't actually be parsed — used to exercise the
2504    /// best-effort fallback.
2505    fn corrupt_emf_header() -> Vec<u8> {
2506        let mut buf = vec![0u8; 44];
2507        buf[0..4].copy_from_slice(&[0x01, 0x00, 0x00, 0x00]);
2508        buf[4..8].copy_from_slice(&[0x2C, 0x00, 0x00, 0x00]);
2509        buf[40..44].copy_from_slice(&[0x20, 0x45, 0x4D, 0x46]);
2510        buf
2511    }
2512
2513    fn png_signature_bytes() -> Vec<u8> {
2514        // 8-byte PNG signature followed by zeros — enough to fail PNG
2515        // decoding but plenty to verify routing decisions.
2516        let mut buf = vec![137, 80, 78, 71, 13, 10, 26, 10];
2517        buf.resize(64, 0);
2518        buf
2519    }
2520
2521    // ---------- is_emf ----------
2522
2523    #[test]
2524    fn is_emf_detects_lowercase_extension() {
2525        assert!(is_emf("word/media/image1.emf", &[]));
2526    }
2527
2528    #[test]
2529    fn is_emf_detects_uppercase_extension() {
2530        assert!(is_emf("word/media/IMAGE1.EMF", &[]));
2531    }
2532
2533    #[test]
2534    fn is_emf_rejects_png_extension() {
2535        assert!(!is_emf("word/media/image1.png", &png_signature_bytes()));
2536    }
2537
2538    #[test]
2539    fn is_emf_rejects_empty_buffer_with_unrelated_extension() {
2540        assert!(!is_emf("word/media/image1.bin", &[]));
2541    }
2542
2543    #[test]
2544    fn is_emf_detects_magic_bytes_without_extension() {
2545        let buf = minimal_valid_emf();
2546        assert!(is_emf("word/media/image1.bin", &buf));
2547    }
2548
2549    #[test]
2550    fn is_emf_rejects_buffer_too_short_for_signature() {
2551        // Has the record-type bytes but is shorter than 44 bytes, so the
2552        // signature check at offset 40 cannot pass.
2553        let buf = vec![0x01, 0x00, 0x00, 0x00];
2554        assert!(!is_emf("x.bin", &buf));
2555    }
2556
2557    #[test]
2558    fn is_emf_rejects_wrong_signature() {
2559        // Right size, right record type, wrong signature bytes.
2560        let mut buf = vec![0u8; 44];
2561        buf[0..4].copy_from_slice(&[0x01, 0x00, 0x00, 0x00]);
2562        buf[40..44].copy_from_slice(&[0xDE, 0xAD, 0xBE, 0xEF]);
2563        assert!(!is_emf("x.bin", &buf));
2564    }
2565
2566    // ---------- add_image routing ----------
2567
2568    #[test]
2569    fn add_image_routes_valid_emf_into_images_with_empty_preview() {
2570        let buf = minimal_valid_emf();
2571        let docx = Docx::new().add_image("rId1", "word/media/image1.emf", buf.clone());
2572
2573        assert_eq!(
2574            docx.images.len(),
2575            1,
2576            "EMF should land in the unified `images` vector"
2577        );
2578        let (id, path, original, preview) = &docx.images[0];
2579        assert_eq!(id, "rId1");
2580        assert_eq!(path, "word/media/image1.emf");
2581        assert_eq!(original.0, buf);
2582        assert!(preview.0.is_empty());
2583    }
2584
2585    #[test]
2586    fn add_image_preserves_emf_detected_by_magic_bytes() {
2587        let buf = corrupt_emf_header();
2588        let docx = Docx::new().add_image("rId1", "word/media/image1.bin", buf.clone());
2589        assert_eq!(docx.images.len(), 1);
2590        let (_, _, original, preview) = &docx.images[0];
2591        assert_eq!(original.0, buf);
2592        assert!(preview.0.is_empty());
2593    }
2594
2595    #[test]
2596    fn add_image_routes_png_as_png_preview() {
2597        // Smallest valid 1x1 RGB PNG, generated via the `image` crate.
2598        let png_bytes = {
2599            let img = image::RgbImage::from_pixel(1, 1, image::Rgb([0, 0, 0]));
2600            let mut cursor = std::io::Cursor::new(Vec::new());
2601            image::DynamicImage::ImageRgb8(img)
2602                .write_to(&mut cursor, image::ImageFormat::Png)
2603                .unwrap();
2604            cursor.into_inner()
2605        };
2606        let _ = png_signature_bytes(); // suppress unused warning if any
2607        let docx = Docx::new().add_image("rId1", "word/media/image1.png", png_bytes);
2608        assert_eq!(docx.images.len(), 1);
2609        let (_, _, _, preview) = &docx.images[0];
2610        // PNG preview starts with the PNG signature, not `<svg`.
2611        assert_eq!(&preview.0[..8], &[137, 80, 78, 71, 13, 10, 26, 10]);
2612    }
2613
2614    // ---------- Docx JSON serialization ----------
2615
2616    #[test]
2617    fn docx_serializes_emf_under_images_field() {
2618        let docx = Docx::new().add_image("rId1", "word/media/image1.emf", minimal_valid_emf());
2619        let json = serde_json::to_string(&docx).expect("should serialize");
2620        // EMF entries appear under the existing `images` key — no
2621        // separate `imagesEmf` key is emitted.
2622        assert!(json.contains("\"images\""));
2623        assert!(
2624            !json.contains("imagesEmf"),
2625            "EMF is unified into `images`; no separate `imagesEmf` key"
2626        );
2627        // The original bytes are base64-serialised inside the tuple.
2628        use base64::Engine;
2629        let b64 = base64::engine::general_purpose::STANDARD.encode(minimal_valid_emf());
2630        assert!(json.contains(&b64));
2631    }
2632}
2633
2634/// Reader-level integration tests for EMF passthrough. We construct a tiny
2635/// in-memory docx (just enough relationships + a media entry) and run it
2636/// through `read_docx` to verify the original bytes surface on `Docx.images`.
2637#[cfg(test)]
2638mod emf_reader_tests {
2639    use super::emf_tests::minimal_valid_emf;
2640    use std::io::Write;
2641
2642    /// Builds a minimum-viable docx ZIP that contains one EMF media
2643    /// file referenced from `document.xml.rels`. The XML payloads are
2644    /// the smallest accepted by the reader; the EMF is the same minimal
2645    /// header/EOF pair used in the unit tests.
2646    fn build_docx_with_emf() -> Vec<u8> {
2647        let buf = std::io::Cursor::new(Vec::<u8>::new());
2648        let mut zip = zip::ZipWriter::new(buf);
2649        let opts: zip::write::FileOptions<'_, ()> =
2650            zip::write::FileOptions::default().compression_method(zip::CompressionMethod::Deflated);
2651
2652        let content_types = r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2653<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
2654  <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
2655  <Default Extension="xml" ContentType="application/xml"/>
2656  <Default Extension="emf" ContentType="image/x-emf"/>
2657  <Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
2658</Types>"#;
2659        let root_rels = r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2660<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
2661  <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
2662</Relationships>"#;
2663        let doc_rels = r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2664<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
2665  <Relationship Id="rId10" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/image1.emf"/>
2666</Relationships>"#;
2667        let document = r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2668<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
2669  <w:body>
2670    <w:p><w:r><w:t>hello</w:t></w:r></w:p>
2671  </w:body>
2672</w:document>"#;
2673
2674        for (path, body) in [
2675            ("[Content_Types].xml", content_types),
2676            ("_rels/.rels", root_rels),
2677            ("word/_rels/document.xml.rels", doc_rels),
2678            ("word/document.xml", document),
2679        ] {
2680            zip.start_file(path, opts).unwrap();
2681            zip.write_all(body.as_bytes()).unwrap();
2682        }
2683        zip.start_file("word/media/image1.emf", opts).unwrap();
2684        zip.write_all(&minimal_valid_emf()).unwrap();
2685
2686        zip.finish().unwrap().into_inner()
2687    }
2688
2689    #[test]
2690    fn read_docx_routes_emf_media_to_images_with_empty_preview() {
2691        let docx_bytes = build_docx_with_emf();
2692        let docx = crate::reader::read_docx(&docx_bytes).expect("should read docx");
2693
2694        assert_eq!(
2695            docx.images.len(),
2696            1,
2697            "EMF media should be routed into the unified `images` vector"
2698        );
2699        let (id, path, original, preview) = &docx.images[0];
2700        assert_eq!(id, "rId10");
2701        assert!(path.ends_with("image1.emf"));
2702        assert_eq!(original.0, minimal_valid_emf());
2703        assert!(preview.0.is_empty());
2704    }
2705}