pptx_rs/shape/
smartartshape.rs1use crate::oxml::shape::{Graphic as OxmlGraphic, GraphicFrame as OxmlFrame, SmartArtRef};
32use crate::shape::base::Shape;
33use crate::units::Emu;
34
35#[derive(Clone, Debug)]
48pub struct SmartArtShape {
49 pub(crate) frame: OxmlFrame,
51}
52
53impl SmartArtShape {
54 pub fn new() -> Self {
60 let smart = SmartArtRef::from_rids("", "", "", "");
61 let frame = OxmlFrame {
62 graphic: OxmlGraphic::SmartArt(smart),
63 ..Default::default()
64 };
65 SmartArtShape { frame }
66 }
67
68 pub fn from_rids(dm_rid: &str, lo_rid: &str, qs_rid: &str, cs_rid: &str) -> Self {
70 let smart = SmartArtRef::from_rids(dm_rid, lo_rid, qs_rid, cs_rid);
71 let frame = OxmlFrame {
72 graphic: OxmlGraphic::SmartArt(smart),
73 ..Default::default()
74 };
75 SmartArtShape { frame }
76 }
77
78 pub fn from_frame(frame: OxmlFrame) -> Self {
80 SmartArtShape { frame }
81 }
82
83 pub fn smart_art(&self) -> Option<&SmartArtRef> {
87 match &self.frame.graphic {
88 OxmlGraphic::SmartArt(s) => Some(s),
89 _ => None,
90 }
91 }
92
93 pub fn smart_art_mut(&mut self) -> Option<&mut SmartArtRef> {
95 match &mut self.frame.graphic {
96 OxmlGraphic::SmartArt(s) => Some(s),
97 _ => None,
98 }
99 }
100
101 pub fn dm_rid(&self) -> &str {
105 self.smart_art()
106 .and_then(|s| s.dm_rid.as_deref())
107 .unwrap_or("")
108 }
109
110 pub fn lo_rid(&self) -> &str {
112 self.smart_art()
113 .and_then(|s| s.lo_rid.as_deref())
114 .unwrap_or("")
115 }
116
117 pub fn qs_rid(&self) -> &str {
119 self.smart_art()
120 .and_then(|s| s.qs_rid.as_deref())
121 .unwrap_or("")
122 }
123
124 pub fn cs_rid(&self) -> &str {
126 self.smart_art()
127 .and_then(|s| s.cs_rid.as_deref())
128 .unwrap_or("")
129 }
130
131 pub fn set_dm_rid(&mut self, rid: impl Into<String>) {
137 if let Some(s) = self.smart_art_mut() {
138 s.dm_rid = Some(rid.into());
139 rebuild_raw_xml(s);
140 }
141 }
142
143 pub fn set_lo_rid(&mut self, rid: impl Into<String>) {
145 if let Some(s) = self.smart_art_mut() {
146 s.lo_rid = Some(rid.into());
147 rebuild_raw_xml(s);
148 }
149 }
150
151 pub fn set_qs_rid(&mut self, rid: impl Into<String>) {
153 if let Some(s) = self.smart_art_mut() {
154 s.qs_rid = Some(rid.into());
155 rebuild_raw_xml(s);
156 }
157 }
158
159 pub fn set_cs_rid(&mut self, rid: impl Into<String>) {
161 if let Some(s) = self.smart_art_mut() {
162 s.cs_rid = Some(rid.into());
163 rebuild_raw_xml(s);
164 }
165 }
166
167 pub fn set_all_rids(
169 &mut self,
170 dm_rid: impl Into<String>,
171 lo_rid: impl Into<String>,
172 qs_rid: impl Into<String>,
173 cs_rid: impl Into<String>,
174 ) {
175 if let Some(s) = self.smart_art_mut() {
176 s.dm_rid = Some(dm_rid.into());
177 s.lo_rid = Some(lo_rid.into());
178 s.qs_rid = Some(qs_rid.into());
179 s.cs_rid = Some(cs_rid.into());
180 rebuild_raw_xml(s);
181 }
182 }
183
184 pub fn set_placeholder(&mut self, ph_idx: u32, ph_type: Option<&str>) {
189 self.frame.is_placeholder = true;
190 self.frame.ph_idx = Some(ph_idx);
191 self.frame.ph_type = ph_type.map(|s| s.to_string());
192 }
193
194 pub fn clear_placeholder(&mut self) {
196 self.frame.is_placeholder = false;
197 self.frame.ph_idx = None;
198 self.frame.ph_type = None;
199 }
200
201 pub fn is_placeholder(&self) -> bool {
203 self.frame.is_placeholder
204 }
205
206 pub fn ph_idx(&self) -> Option<u32> {
208 self.frame.ph_idx
209 }
210
211 pub fn ph_type(&self) -> Option<&str> {
213 self.frame.ph_type.as_deref()
214 }
215}
216
217impl Default for SmartArtShape {
218 fn default() -> Self {
219 Self::new()
220 }
221}
222
223fn rebuild_raw_xml(s: &mut SmartArtRef) {
228 let dm = s.dm_rid.as_deref().unwrap_or("");
229 let lo = s.lo_rid.as_deref().unwrap_or("");
230 let qs = s.qs_rid.as_deref().unwrap_or("");
231 let cs = s.cs_rid.as_deref().unwrap_or("");
232 s.raw_xml = SmartArtRef::from_rids(dm, lo, qs, cs).raw_xml;
233}
234
235impl Shape for SmartArtShape {
236 fn id(&self) -> u32 {
237 self.frame.id
238 }
239 fn set_id(&mut self, id: u32) {
240 self.frame.id = id;
241 }
242 fn name(&self) -> &str {
243 &self.frame.name
244 }
245 fn set_name(&mut self, name: String) {
246 self.frame.name = name;
247 }
248 fn shape_type(&self) -> &'static str {
249 "smart_art"
250 }
251
252 fn left(&self) -> Emu {
253 self.frame.properties.xfrm.off_x.unwrap_or_default()
254 }
255 fn set_left(&mut self, emu: Emu) {
256 self.frame.properties.xfrm.off_x = Some(emu);
257 }
258 fn top(&self) -> Emu {
259 self.frame.properties.xfrm.off_y.unwrap_or_default()
260 }
261 fn set_top(&mut self, emu: Emu) {
262 self.frame.properties.xfrm.off_y = Some(emu);
263 }
264 fn width(&self) -> Emu {
265 self.frame.properties.xfrm.ext_cx.unwrap_or_default()
266 }
267 fn set_width(&mut self, emu: Emu) {
268 self.frame.properties.xfrm.ext_cx = Some(emu);
269 }
270 fn height(&self) -> Emu {
271 self.frame.properties.xfrm.ext_cy.unwrap_or_default()
272 }
273 fn set_height(&mut self, emu: Emu) {
274 self.frame.properties.xfrm.ext_cy = Some(emu);
275 }
276
277 fn rotation(&self) -> f64 {
279 0.0
280 }
281 fn set_rotation(&mut self, _deg: f64) {}
282}
283
284#[cfg(test)]
285mod tests {
286 use super::*;
287
288 #[test]
290 fn new_smartart_shape_basics() {
291 let s = SmartArtShape::new();
292 assert_eq!(s.dm_rid(), "");
293 assert_eq!(s.lo_rid(), "");
294 assert_eq!(s.qs_rid(), "");
295 assert_eq!(s.cs_rid(), "");
296 assert!(s.smart_art().unwrap().raw_xml.contains("dgm:relIds"));
298 }
299
300 #[test]
302 fn from_rids_works() {
303 let s = SmartArtShape::from_rids("rId1", "rId2", "rId3", "rId4");
304 assert_eq!(s.dm_rid(), "rId1");
305 assert_eq!(s.lo_rid(), "rId2");
306 assert_eq!(s.qs_rid(), "rId3");
307 assert_eq!(s.cs_rid(), "rId4");
308 let xml = &s.smart_art().unwrap().raw_xml;
309 assert!(xml.contains("r:dm=\"rId1\""));
310 assert!(xml.contains("r:lo=\"rId2\""));
311 assert!(xml.contains("r:qs=\"rId3\""));
312 assert!(xml.contains("r:cs=\"rId4\""));
313 }
314
315 #[test]
317 fn set_single_rid_rebuilds_raw_xml() {
318 let mut s = SmartArtShape::from_rids("rId1", "rId2", "rId3", "rId4");
319 s.set_dm_rid("rIdDmNew");
320 assert_eq!(s.dm_rid(), "rIdDmNew");
321 assert_eq!(s.lo_rid(), "rId2");
323 assert_eq!(s.qs_rid(), "rId3");
324 assert_eq!(s.cs_rid(), "rId4");
325 let xml = &s.smart_art().unwrap().raw_xml;
327 assert!(xml.contains("r:dm=\"rIdDmNew\""));
328 assert!(xml.contains("r:lo=\"rId2\""));
329 }
330
331 #[test]
333 fn set_all_rids_works() {
334 let mut s = SmartArtShape::new();
335 s.set_all_rids("a", "b", "c", "d");
336 assert_eq!(s.dm_rid(), "a");
337 assert_eq!(s.lo_rid(), "b");
338 assert_eq!(s.qs_rid(), "c");
339 assert_eq!(s.cs_rid(), "d");
340 let xml = &s.smart_art().unwrap().raw_xml;
341 assert!(xml.contains("r:dm=\"a\""));
342 assert!(xml.contains("r:cs=\"d\""));
343 }
344
345 #[test]
347 fn set_placeholder_works() {
348 let mut s = SmartArtShape::new();
349 assert!(!s.is_placeholder());
350 s.set_placeholder(0, Some("obj"));
351 assert!(s.is_placeholder());
352 assert_eq!(s.ph_idx(), Some(0));
353 assert_eq!(s.ph_type(), Some("obj"));
354 s.clear_placeholder();
355 assert!(!s.is_placeholder());
356 assert_eq!(s.ph_idx(), None);
357 }
358
359 #[test]
361 fn shape_trait_geometry() {
362 let mut s = SmartArtShape::new();
363 s.set_left(crate::Emu(100));
364 s.set_top(crate::Emu(200));
365 s.set_width(crate::Emu(300));
366 s.set_height(crate::Emu(400));
367 assert_eq!(s.left(), crate::Emu(100));
368 assert_eq!(s.top(), crate::Emu(200));
369 assert_eq!(s.width(), crate::Emu(300));
370 assert_eq!(s.height(), crate::Emu(400));
371 s.set_rotation(45.0);
373 assert_eq!(s.rotation(), 0.0);
374 }
375}