Skip to main content

docx_rs/reader/
rels.rs

1use super::*;
2use crate::reader::{FromXML, ReaderError};
3use std::str::FromStr;
4use std::{
5    collections::{BTreeMap, BTreeSet},
6    io::Read,
7    path::{Path, PathBuf},
8};
9use xml::reader::{EventReader, XmlEvent};
10
11pub type ReadRels = BTreeMap<String, BTreeSet<(RId, PathBuf, Option<String>)>>;
12
13impl FromXML for Rels {
14    fn from_xml<R: Read>(reader: R) -> Result<Self, ReaderError> {
15        let parser = EventReader::new(reader);
16        let mut s = Self::default();
17        let mut depth = 0;
18        for e in parser {
19            match e {
20                Ok(XmlEvent::StartElement { attributes, .. }) => {
21                    if depth == 1 {
22                        let mut rel_type = "".to_owned();
23                        let mut target = "".to_owned();
24                        let mut id = "".to_owned();
25                        for attr in attributes {
26                            let name: &str = &attr.name.local_name;
27                            if name == "Type" {
28                                rel_type = attr.value.clone();
29                            } else if name == "Target" {
30                                target = attr.value.clone();
31                            } else if name == "Id" {
32                                id = attr.value.clone();
33                            }
34                        }
35                        // Preserve original relationship ID
36                        s.rels.push((rel_type, id, target));
37                    }
38                    depth += 1;
39                }
40                Ok(XmlEvent::EndElement { .. }) => {
41                    depth -= 1;
42                }
43                Err(_) => return Err(ReaderError::XMLReadError),
44                _ => {}
45            }
46        }
47        Ok(s)
48    }
49}
50
51pub fn find_rels_filename(main_path: impl AsRef<Path>) -> Result<PathBuf, ReaderError> {
52    let path = main_path.as_ref();
53    let dir = path
54        .parent()
55        .ok_or(ReaderError::DocumentRelsNotFoundError)?;
56    let base = path
57        .file_stem()
58        .ok_or(ReaderError::DocumentRelsNotFoundError)?;
59    Ok(Path::new(dir)
60        .join("_rels")
61        .join(base)
62        .with_extension("xml.rels"))
63}
64
65pub fn read_rels_xml<R: Read>(reader: R, dir: impl AsRef<Path>) -> Result<ReadRels, ReaderError> {
66    let mut parser = EventReader::new(reader);
67    let mut rels: BTreeMap<String, BTreeSet<(RId, PathBuf, Option<String>)>> = BTreeMap::new();
68
69    loop {
70        let e = parser.next();
71        match e {
72            Ok(XmlEvent::StartElement {
73                attributes, name, ..
74            }) => {
75                let e = XMLElement::from_str(&name.local_name).unwrap();
76                if let XMLElement::Relationship = e {
77                    let mut rel_type = "".to_owned();
78                    let mut rid = "".to_owned();
79                    let mut target_mode = None;
80                    let mut target_string = "".to_owned();
81                    for a in attributes {
82                        let local_name = &a.name.local_name;
83                        if local_name == "Type" {
84                            rel_type = a.value.to_owned();
85                        } else if local_name == "Target" {
86                            // target_str = Path::new(dir.as_ref()).join(a.value);
87                            target_string = a.value.to_owned();
88                        } else if local_name == "Id" {
89                            rid = a.value.to_owned();
90                        } else if local_name == "TargetMode" {
91                            target_mode = Some(a.value.to_owned());
92                        }
93                    }
94
95                    let target = if !rel_type.ends_with("hyperlink") {
96                        Path::new(dir.as_ref()).join(target_string)
97                    } else {
98                        Path::new("").join(target_string)
99                    };
100
101                    let current = rels.remove(&rel_type);
102                    if let Some(mut paths) = current {
103                        paths.insert((rid, target, target_mode));
104                        rels.insert(rel_type, paths);
105                    } else {
106                        let s: BTreeSet<(RId, PathBuf, Option<String>)> =
107                            vec![(rid, target, target_mode)].into_iter().collect();
108                        rels.insert(rel_type, s);
109                    }
110                    continue;
111                }
112            }
113            Ok(XmlEvent::EndElement { name, .. }) => {
114                let e = XMLElement::from_str(&name.local_name).unwrap();
115                if let XMLElement::Relationships = e {
116                    break;
117                }
118            }
119            Err(_) => return Err(ReaderError::XMLReadError),
120            _ => {}
121        }
122    }
123    Ok(rels)
124}
125
126#[cfg(test)]
127mod tests {
128
129    use super::*;
130    #[cfg(test)]
131    use pretty_assertions::assert_eq;
132
133    #[test]
134    fn test_from_xml() {
135        let xml = r#"<?xml version="1.0" encoding="UTF-8"?>
136<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
137  <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml" />
138</Relationships>"#;
139        let c = Rels::from_xml(xml.as_bytes()).unwrap();
140        let rels =
141            vec![
142        (
143            "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"
144                .to_owned(),
145            "rId1".to_owned(),
146            "docProps/core.xml".to_owned(),
147        )];
148        assert_eq!(Rels { rels }, c);
149    }
150}