1use crate::generated::*;
7use ooxml_dml::ext::{TextBodyExt, TextParagraphExt};
8use ooxml_dml::types::TextParagraph;
9
10pub trait ShapeExt {
12 fn name(&self) -> &str;
14
15 fn description(&self) -> Option<&str>;
17
18 fn text_body(&self) -> Option<&ooxml_dml::types::TextBody>;
20
21 fn paragraphs(&self) -> &[TextParagraph];
23
24 fn text(&self) -> Option<String>;
26
27 fn has_text(&self) -> bool;
29
30 fn shape_id(&self) -> u32;
32
33 fn is_placeholder(&self) -> bool;
35
36 fn placeholder_type(&self) -> Option<&STPlaceholderType>;
38
39 fn placeholder_index(&self) -> Option<u32>;
41
42 fn offset_emu(&self) -> Option<(i64, i64)>;
44
45 fn extent_emu(&self) -> Option<(i64, i64)>;
47
48 fn rotation_angle_deg(&self) -> Option<f64>;
50}
51
52impl ShapeExt for Shape {
53 fn name(&self) -> &str {
54 &self.non_visual_properties.c_nv_pr.name
55 }
56
57 fn description(&self) -> Option<&str> {
58 self.non_visual_properties.c_nv_pr.descr.as_deref()
59 }
60
61 fn text_body(&self) -> Option<&ooxml_dml::types::TextBody> {
62 self.text_body.as_deref()
63 }
64
65 fn paragraphs(&self) -> &[TextParagraph] {
66 static EMPTY: &[TextParagraph] = &[];
67 self.text_body
68 .as_ref()
69 .map(|tb| tb.paragraphs())
70 .unwrap_or(EMPTY)
71 }
72
73 fn text(&self) -> Option<String> {
74 self.text_body.as_ref().map(|tb| {
75 tb.paragraphs()
76 .iter()
77 .map(|p| p.text())
78 .collect::<Vec<_>>()
79 .join("\n")
80 })
81 }
82
83 fn has_text(&self) -> bool {
84 self.text_body
85 .as_ref()
86 .is_some_and(|tb| !tb.paragraphs().is_empty())
87 }
88
89 fn shape_id(&self) -> u32 {
90 self.non_visual_properties.c_nv_pr.id
91 }
92
93 fn is_placeholder(&self) -> bool {
94 self.non_visual_properties.nv_pr.ph.is_some()
95 }
96
97 fn placeholder_type(&self) -> Option<&STPlaceholderType> {
98 self.non_visual_properties
99 .nv_pr
100 .ph
101 .as_deref()
102 .and_then(|p| p.r#type.as_ref())
103 }
104
105 fn placeholder_index(&self) -> Option<u32> {
106 self.non_visual_properties
107 .nv_pr
108 .ph
109 .as_deref()
110 .and_then(|p| p.idx)
111 }
112
113 fn offset_emu(&self) -> Option<(i64, i64)> {
114 use ooxml_dml::ext::ShapePropertiesExt;
115 self.shape_properties.offset_emu()
116 }
117
118 fn extent_emu(&self) -> Option<(i64, i64)> {
119 use ooxml_dml::ext::ShapePropertiesExt;
120 self.shape_properties.extent_emu()
121 }
122
123 fn rotation_angle_deg(&self) -> Option<f64> {
124 use ooxml_dml::ext::ShapePropertiesExt;
125 self.shape_properties.rotation_angle_deg()
126 }
127}
128
129pub trait PictureExt {
131 fn name(&self) -> &str;
133
134 fn description(&self) -> Option<&str>;
136
137 fn embed_rel_id(&self) -> Option<&str>;
139
140 fn offset_emu(&self) -> Option<(i64, i64)>;
142
143 fn extent_emu(&self) -> Option<(i64, i64)>;
145
146 #[cfg(feature = "dml-fills")]
151 fn crop_rect(&self) -> Option<&ooxml_dml::types::CTRelativeRect>;
152}
153
154impl PictureExt for Picture {
155 fn name(&self) -> &str {
156 &self.non_visual_picture_properties.c_nv_pr.name
157 }
158
159 fn description(&self) -> Option<&str> {
160 self.non_visual_picture_properties.c_nv_pr.descr.as_deref()
161 }
162
163 fn embed_rel_id(&self) -> Option<&str> {
164 self.blip_fill
165 .blip
166 .as_ref()
167 .and_then(|b| b.embed.as_deref())
168 }
169
170 fn offset_emu(&self) -> Option<(i64, i64)> {
171 use ooxml_dml::ext::ShapePropertiesExt;
172 self.shape_properties.offset_emu()
173 }
174
175 fn extent_emu(&self) -> Option<(i64, i64)> {
176 use ooxml_dml::ext::ShapePropertiesExt;
177 self.shape_properties.extent_emu()
178 }
179
180 #[cfg(feature = "dml-fills")]
181 fn crop_rect(&self) -> Option<&ooxml_dml::types::CTRelativeRect> {
182 self.blip_fill.src_rect.as_deref()
183 }
184}
185
186pub trait ConnectorExt {
188 fn name(&self) -> &str;
190
191 fn description(&self) -> Option<&str>;
193}
194
195impl ConnectorExt for Connector {
196 fn name(&self) -> &str {
197 &self.non_visual_connector_properties.c_nv_pr.name
198 }
199
200 fn description(&self) -> Option<&str> {
201 self.non_visual_connector_properties
202 .c_nv_pr
203 .descr
204 .as_deref()
205 }
206}
207
208pub trait GraphicalObjectFrameExt {
210 fn name(&self) -> &str;
212
213 fn description(&self) -> Option<&str>;
215}
216
217impl GraphicalObjectFrameExt for GraphicalObjectFrame {
218 fn name(&self) -> &str {
219 &self.nv_graphic_frame_pr.c_nv_pr.name
220 }
221
222 fn description(&self) -> Option<&str> {
223 self.nv_graphic_frame_pr.c_nv_pr.descr.as_deref()
224 }
225}
226
227pub trait GroupShapeExt {
232 fn name(&self) -> &str;
234
235 fn description(&self) -> Option<&str>;
237
238 fn shapes(&self) -> &[Shape];
240
241 fn pictures(&self) -> &[Picture];
243
244 fn connectors(&self) -> &[Connector];
246
247 fn group_shapes(&self) -> &[GroupShape];
249
250 fn graphic_frames(&self) -> &[GraphicalObjectFrame];
252
253 fn text(&self) -> String;
255
256 fn all_shapes_recursive(&self) -> Vec<&Shape>;
258
259 fn all_text_recursive(&self) -> String;
261}
262
263impl GroupShapeExt for GroupShape {
264 fn name(&self) -> &str {
265 &self.non_visual_group_properties.c_nv_pr.name
266 }
267
268 fn description(&self) -> Option<&str> {
269 self.non_visual_group_properties.c_nv_pr.descr.as_deref()
270 }
271
272 fn shapes(&self) -> &[Shape] {
273 &self.shape
274 }
275
276 fn pictures(&self) -> &[Picture] {
277 &self.picture
278 }
279
280 fn connectors(&self) -> &[Connector] {
281 &self.connector
282 }
283
284 fn group_shapes(&self) -> &[GroupShape] {
285 &self.group_shape
286 }
287
288 fn graphic_frames(&self) -> &[GraphicalObjectFrame] {
289 &self.graphic_frame
290 }
291
292 fn text(&self) -> String {
293 let mut texts = Vec::new();
294
295 for shape in &self.shape {
297 if let Some(t) = shape.text() {
298 texts.push(t);
299 }
300 }
301
302 for group in &self.group_shape {
304 let t = group.text();
305 if !t.is_empty() {
306 texts.push(t);
307 }
308 }
309
310 texts.join("\n")
311 }
312
313 fn all_shapes_recursive(&self) -> Vec<&Shape> {
314 let mut shapes: Vec<&Shape> = self.shape.iter().collect();
315 for group in &self.group_shape {
316 shapes.extend(group.all_shapes_recursive());
317 }
318 shapes
319 }
320
321 fn all_text_recursive(&self) -> String {
322 self.all_shapes_recursive()
323 .iter()
324 .filter_map(|s| s.text())
325 .filter(|t| !t.is_empty())
326 .collect::<Vec<_>>()
327 .join("\n")
328 }
329}
330
331pub trait CommonSlideDataExt {
333 fn shape_tree(&self) -> &GroupShape;
335
336 fn shapes(&self) -> &[Shape];
338
339 fn pictures(&self) -> &[Picture];
341
342 fn text(&self) -> String;
344}
345
346impl CommonSlideDataExt for CommonSlideData {
347 fn shape_tree(&self) -> &GroupShape {
348 &self.shape_tree
349 }
350
351 fn shapes(&self) -> &[Shape] {
352 self.shape_tree.shapes()
353 }
354
355 fn pictures(&self) -> &[Picture] {
356 self.shape_tree.pictures()
357 }
358
359 fn text(&self) -> String {
360 self.shape_tree.text()
361 }
362}
363
364pub trait SlideExt {
366 fn common_slide_data(&self) -> &CommonSlideData;
368
369 fn shape_tree(&self) -> &GroupShape;
371
372 fn shapes(&self) -> &[Shape];
374
375 fn pictures(&self) -> &[Picture];
377
378 fn text(&self) -> String;
380
381 #[cfg(feature = "pml-transitions")]
383 fn transition(&self) -> Option<&SlideTransition>;
384
385 #[cfg(feature = "pml-styling")]
389 fn background(&self) -> Option<&CTBackground>;
390
391 fn is_hidden(&self) -> bool;
393}
394
395impl SlideExt for Slide {
396 fn common_slide_data(&self) -> &CommonSlideData {
397 &self.common_slide_data
398 }
399
400 fn shape_tree(&self) -> &GroupShape {
401 self.common_slide_data.shape_tree()
402 }
403
404 fn shapes(&self) -> &[Shape] {
405 self.common_slide_data.shapes()
406 }
407
408 fn pictures(&self) -> &[Picture] {
409 self.common_slide_data.pictures()
410 }
411
412 fn text(&self) -> String {
413 self.common_slide_data.text()
414 }
415
416 #[cfg(feature = "pml-transitions")]
417 fn transition(&self) -> Option<&SlideTransition> {
418 self.transition.as_deref()
419 }
420
421 #[cfg(feature = "pml-styling")]
422 fn background(&self) -> Option<&CTBackground> {
423 self.common_slide_data.bg.as_deref()
424 }
425
426 fn is_hidden(&self) -> bool {
427 !self.show.unwrap_or(true)
428 }
429}
430
431pub trait SlideLayoutExt {
433 fn common_slide_data(&self) -> &CommonSlideData;
435
436 fn layout_type(&self) -> Option<&STSlideLayoutType>;
438
439 fn show_master_shapes(&self) -> bool;
441}
442
443impl SlideLayoutExt for SlideLayout {
444 fn common_slide_data(&self) -> &CommonSlideData {
445 &self.common_slide_data
446 }
447
448 #[cfg(feature = "pml-masters")]
449 fn layout_type(&self) -> Option<&STSlideLayoutType> {
450 self.r#type.as_ref()
451 }
452
453 #[cfg(not(feature = "pml-masters"))]
454 fn layout_type(&self) -> Option<&STSlideLayoutType> {
455 None
456 }
457
458 #[cfg(feature = "pml-masters")]
459 fn show_master_shapes(&self) -> bool {
460 self.show_master_sp.unwrap_or(true)
461 }
462
463 #[cfg(not(feature = "pml-masters"))]
464 fn show_master_shapes(&self) -> bool {
465 true
466 }
467}
468
469pub trait SlideMasterExt {
471 fn common_slide_data(&self) -> &CommonSlideData;
473
474 fn preserve(&self) -> bool;
476}
477
478impl SlideMasterExt for SlideMaster {
479 fn common_slide_data(&self) -> &CommonSlideData {
480 &self.common_slide_data
481 }
482
483 #[cfg(feature = "pml-masters")]
484 fn preserve(&self) -> bool {
485 self.preserve.unwrap_or(false)
486 }
487
488 #[cfg(not(feature = "pml-masters"))]
489 fn preserve(&self) -> bool {
490 false
491 }
492}
493
494#[cfg(feature = "pml-notes")]
496pub trait NotesSlideExt {
497 fn common_slide_data(&self) -> &CommonSlideData;
499
500 fn text(&self) -> String;
502}
503
504#[cfg(feature = "pml-notes")]
505impl NotesSlideExt for NotesSlide {
506 fn common_slide_data(&self) -> &CommonSlideData {
507 &self.common_slide_data
508 }
509
510 fn text(&self) -> String {
511 self.common_slide_data.text()
512 }
513}