docx_rs/documents/elements/
footnote.rs

1use serde::Serialize;
2use std::io::Write;
3
4use crate::documents::*;
5use crate::xml_builder::*;
6use footnote_id::generate_footnote_id;
7
8#[derive(Serialize, Debug, Clone, PartialEq)]
9#[serde(rename_all = "camelCase")]
10pub struct Footnote {
11    pub id: usize,
12    pub content: Vec<Paragraph>,
13}
14
15impl Default for Footnote {
16    fn default() -> Self {
17        Footnote {
18            id: 1,
19            content: vec![],
20        }
21    }
22}
23
24impl Footnote {
25    pub fn new() -> Self {
26        Self {
27            id: generate_footnote_id(),
28            ..Default::default()
29        }
30    }
31
32    pub fn id(&self) -> usize {
33        self.id
34    }
35
36    pub fn add_content(&mut self, p: Paragraph) -> Self {
37        self.content.push(p);
38        self.clone()
39    }
40}
41impl From<&FootnoteReference> for Footnote {
42    fn from(reference: &FootnoteReference) -> Self {
43        Footnote {
44            id: reference.id,
45            content: reference.content.clone(),
46        }
47    }
48}
49
50impl BuildXML for Footnote {
51    fn build_to<W: Write>(
52        &self,
53        stream: xml::writer::EventWriter<W>,
54    ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
55        // To ensure docx compatible XML serialization for footnotes, we default to an empty paragraph.
56        let mut footnote = self.clone();
57        if self.content == vec![] {
58            eprintln!("{:?}", footnote);
59            footnote.add_content(Paragraph::new());
60        }
61
62        XMLBuilder::from(stream)
63            .open_footnote(&format!("{}", self.id))?
64            .add_children(&footnote.content)?
65            .close()?
66            .into_inner()
67    }
68}
69
70#[cfg(test)]
71mod tests {
72
73    use super::*;
74    #[cfg(test)]
75    use pretty_assertions::assert_eq;
76    use std::str;
77
78    #[test]
79    fn test_footnote_build_default() {
80        let b = Footnote::new().build();
81        assert_eq!(
82            str::from_utf8(&b).unwrap(),
83            r#"<w:footnote w:id="1"><w:p w14:paraId="12345678"><w:pPr><w:rPr /></w:pPr></w:p></w:footnote>"#
84        );
85    }
86
87    #[test]
88    fn test_footnote_build_with_paragraph() {
89        let b = Footnote::new()
90            .add_content(Paragraph::new().add_run(Run::new().add_text("hello")))
91            .build();
92        assert_eq!(
93            str::from_utf8(&b).unwrap(),
94            r#"<w:footnote w:id="1"><w:p w14:paraId="12345678"><w:pPr><w:rPr /></w:pPr><w:r><w:rPr /><w:t xml:space="preserve">hello</w:t></w:r></w:p></w:footnote>"#
95        );
96    }
97}