1use super::Refused;
15use super::block::{declares, name_for};
16use super::format::{automatic_styles, element};
17use crate::style::{Family, Styles};
18use crate::xml::{Element, Node, Ns};
19
20#[derive(Debug, Clone, PartialEq, Eq)]
22pub enum Description {
23 Text(String),
25 Decorative,
27 Missing,
29}
30
31pub fn is_figure(frame: &Element) -> bool {
33 frame.is(&Ns::Draw, "frame") && frame.child(&Ns::Draw, "image").is_some()
34}
35
36pub fn figures(root: &Element) -> Vec<Vec<usize>> {
41 let mut found = Vec::new();
42 collect(root, &mut Vec::new(), &mut found);
43 found
44}
45
46fn collect(parent: &Element, path: &mut Vec<usize>, into: &mut Vec<Vec<usize>>) {
47 for (index, child) in parent.elements_indexed() {
48 if child.is(&Ns::Text, "note") {
49 continue;
50 }
51 path.push(index);
52 if is_figure(child) {
53 into.push(path.clone());
54 } else {
55 collect(child, path, into);
56 }
57 path.pop();
58 }
59}
60
61pub fn description(frame: &Element, styles: &Styles) -> Description {
66 let decorative = frame
67 .attr(&Ns::Draw, "style-name")
68 .map(|name| styles.resolve(&Family::Graphic, name))
69 .and_then(|properties| properties.graphic.decorative)
70 .unwrap_or(false);
71 if decorative {
72 return Description::Decorative;
73 }
74 ["title", "desc"]
75 .into_iter()
76 .filter_map(|local| frame.child(&Ns::Svg, local))
77 .map(|text| text.plain_text().trim().to_owned())
78 .find(|text| !text.is_empty())
79 .map_or(Description::Missing, Description::Text)
80}
81
82pub fn set_alternative_text(
92 content: &mut Element,
93 path: &[usize],
94 text: &str,
95) -> Result<(), Refused> {
96 content
97 .at(path)
98 .filter(|e| is_figure(e))
99 .ok_or(Refused::NotFound)?;
100 if !content.declare(&Ns::Svg) {
101 return Err(Refused::Namespace);
102 }
103 let mut title = element(content.name_for(&Ns::Svg, "title"));
104 title.children.push(Node::Text(text.to_owned()));
105 title.self_closing = false;
106
107 let frame = content.at_mut(path).ok_or(Refused::NotFound)?;
108 let existing = frame
109 .children
110 .iter()
111 .position(|n| matches!(n, Node::Element(e) if e.is(&Ns::Svg, "title")));
112 if let Some(at) = existing {
113 frame.children[at] = Node::Element(title);
114 } else {
115 let at = frame
118 .children
119 .iter()
120 .position(|n| {
121 matches!(n, Node::Element(e)
122 if e.is(&Ns::Svg, "desc")
123 || e.is(&Ns::Draw, "contour-polygon")
124 || e.is(&Ns::Draw, "contour-path"))
125 })
126 .unwrap_or(frame.children.len());
127 frame.children.insert(at, Node::Element(title));
128 frame.self_closing = false;
129 }
130 Ok(())
131}
132
133pub fn set_decorative(
150 content: &mut Element,
151 path: &[usize],
152 styles: &mut Styles,
153) -> Result<(), Refused> {
154 let frame = content
155 .at(path)
156 .filter(|e| is_figure(e))
157 .ok_or(Refused::NotFound)?;
158 if description(frame, styles) == Description::Decorative {
159 return Ok(());
160 }
161 let current = frame.attr(&Ns::Draw, "style-name").map(ToOwned::to_owned);
162 declares(content, &[Ns::Draw, Ns::Style])?;
163 if !content.declare(&Ns::Loext) {
164 return Err(Refused::Namespace);
165 }
166
167 let automatic = current.as_deref().and_then(|name| {
168 content
169 .child(&Ns::Office, "automatic-styles")?
170 .elements()
171 .find(|s| {
172 s.is(&Ns::Style, "style")
173 && s.attr(&Ns::Style, "family") == Some("graphic")
174 && s.attr(&Ns::Style, "name") == Some(name)
175 })
176 .cloned()
177 });
178 let mut style = if let Some(mut copy) = automatic {
179 copy.remove_attr(&Ns::Style, "name");
180 copy
181 } else {
182 let mut style = element(content.name_for(&Ns::Style, "style"));
183 style.set_attr(content.name_for(&Ns::Style, "family"), "graphic");
184 if let Some(parent) = ¤t {
185 style.set_attr(content.name_for(&Ns::Style, "parent-style-name"), parent);
186 }
187 style
188 };
189 let properties = if let Some(at) = style
190 .children
191 .iter()
192 .position(|n| matches!(n, Node::Element(e) if e.is(&Ns::Style, "graphic-properties")))
193 {
194 at
195 } else {
196 style.children.push(Node::Element(element(
197 content.name_for(&Ns::Style, "graphic-properties"),
198 )));
199 style.self_closing = false;
200 style.children.len() - 1
201 };
202 if let Node::Element(properties) = &mut style.children[properties] {
203 properties.set_attr(content.name_for(&Ns::Loext, "decorative"), "true");
204 }
205
206 let (name, fresh) = name_for(content, styles, style, &Family::Graphic, "gr");
207 let name_attr = content.name_for(&Ns::Draw, "style-name");
208 content
209 .at_mut(path)
210 .ok_or(Refused::NotFound)?
211 .set_attr(name_attr, name);
212 if let Some(fresh) = fresh {
213 let container = automatic_styles(content);
214 container.children.push(Node::Element(fresh));
215 container.self_closing = false;
216 }
217 Ok(())
218}