Skip to main content

pptx/shapes/
mod.rs

1pub mod action;
2pub mod autoshape;
3pub mod connector;
4pub mod freeform;
5pub mod graphfrm;
6pub mod group;
7pub mod ole;
8pub mod parser;
9pub mod picture;
10pub mod placeholder;
11mod shape_accessors;
12pub mod shapetree;
13
14pub use action::{ActionSetting, Hyperlink};
15pub use autoshape::AutoShape;
16pub use connector::Connector;
17pub use freeform::FreeformBuilder;
18pub use graphfrm::GraphicFrame;
19pub use group::GroupShape;
20pub use ole::OleObject;
21pub use picture::Picture;
22pub use placeholder::PlaceholderFormat;
23pub use shapetree::ShapeTree;
24
25use std::fmt;
26
27use crate::units::{Emu, ShapeId};
28use crate::xml_util::WriteXml;
29
30/// Common geometric and identity properties shared by all shape types.
31pub trait ShapeProperties {
32    /// Returns the unique shape identifier.
33    fn shape_id(&self) -> ShapeId;
34    /// Returns the shape name.
35    fn name(&self) -> &str;
36    /// Returns the left position in EMU (English Metric Units).
37    fn left(&self) -> Emu;
38    /// Returns the top position in EMU (English Metric Units).
39    fn top(&self) -> Emu;
40    /// Returns the width in EMU (English Metric Units).
41    fn width(&self) -> Emu;
42    /// Returns the height in EMU (English Metric Units).
43    fn height(&self) -> Emu;
44    /// Returns the rotation angle in degrees.
45    fn rotation(&self) -> f64;
46}
47
48/// Implement `ShapeProperties` for a struct whose fields are named identically.
49macro_rules! impl_shape_properties {
50    ($ty:ty) => {
51        impl ShapeProperties for $ty {
52            #[inline]
53            fn shape_id(&self) -> ShapeId {
54                self.shape_id
55            }
56            #[inline]
57            fn name(&self) -> &str {
58                &self.name
59            }
60            #[inline]
61            fn left(&self) -> Emu {
62                self.left
63            }
64            #[inline]
65            fn top(&self) -> Emu {
66                self.top
67            }
68            #[inline]
69            fn width(&self) -> Emu {
70                self.width
71            }
72            #[inline]
73            fn height(&self) -> Emu {
74                self.height
75            }
76            #[inline]
77            fn rotation(&self) -> f64 {
78                self.rotation
79            }
80        }
81    };
82}
83
84impl_shape_properties!(AutoShape);
85impl_shape_properties!(Picture);
86impl_shape_properties!(GraphicFrame);
87impl_shape_properties!(GroupShape);
88impl_shape_properties!(Connector);
89impl_shape_properties!(OleObject);
90
91/// A shape on a slide.
92///
93/// This enum wraps the different kinds of shape that can appear on a slide.
94#[non_exhaustive]
95#[derive(Debug, Clone, PartialEq)]
96pub enum Shape {
97    AutoShape(Box<AutoShape>),
98    Picture(Box<Picture>),
99    GraphicFrame(Box<GraphicFrame>),
100    GroupShape(Box<GroupShape>),
101    Connector(Connector),
102    OleObject(OleObject),
103}
104
105/// Dispatch a method call to the inner shape type for all variants.
106macro_rules! dispatch_shape {
107    ($self:expr, $method:ident) => {
108        match $self {
109            Shape::AutoShape(s) => s.$method(),
110            Shape::Picture(s) => s.$method(),
111            Shape::GraphicFrame(s) => s.$method(),
112            Shape::GroupShape(s) => s.$method(),
113            Shape::Connector(s) => s.$method(),
114            Shape::OleObject(s) => s.$method(),
115        }
116    };
117}
118
119impl Shape {
120    /// Returns the unique shape identifier.
121    #[inline]
122    #[must_use]
123    pub fn shape_id(&self) -> ShapeId {
124        dispatch_shape!(self, shape_id)
125    }
126    /// Returns the shape name.
127    #[inline]
128    #[must_use]
129    pub fn name(&self) -> &str {
130        dispatch_shape!(self, name)
131    }
132    /// Returns the left position in EMU (English Metric Units).
133    #[inline]
134    pub fn left(&self) -> Emu {
135        dispatch_shape!(self, left)
136    }
137    /// Returns the top position in EMU (English Metric Units).
138    #[inline]
139    pub fn top(&self) -> Emu {
140        dispatch_shape!(self, top)
141    }
142    /// Returns the width in EMU (English Metric Units).
143    #[inline]
144    pub fn width(&self) -> Emu {
145        dispatch_shape!(self, width)
146    }
147    /// Returns the height in EMU (English Metric Units).
148    #[inline]
149    pub fn height(&self) -> Emu {
150        dispatch_shape!(self, height)
151    }
152    /// Returns the rotation angle in degrees.
153    #[inline]
154    #[must_use]
155    pub fn rotation(&self) -> f64 {
156        dispatch_shape!(self, rotation)
157    }
158
159    /// Returns `true` if this shape contains a text frame.
160    #[inline]
161    #[must_use]
162    pub const fn has_text_frame(&self) -> bool {
163        match self {
164            Self::AutoShape(a) => a.has_text_frame(),
165            _ => false,
166        }
167    }
168
169    /// Returns `true` if this shape contains a table.
170    #[inline]
171    #[must_use]
172    pub fn has_table(&self) -> bool {
173        matches!(self, Self::GraphicFrame(g) if g.has_table)
174    }
175
176    /// Returns `true` if this shape is a placeholder.
177    #[inline]
178    #[must_use]
179    pub const fn is_placeholder(&self) -> bool {
180        match self {
181            Self::AutoShape(s) => s.placeholder.is_some(),
182            Self::Picture(s) => s.placeholder.is_some(),
183            Self::GraphicFrame(s) => s.placeholder.is_some(),
184            Self::GroupShape(_) | Self::Connector(_) | Self::OleObject(_) => false,
185        }
186    }
187
188    /// Get the placeholder format for this shape, if it is a placeholder.
189    #[inline]
190    #[must_use]
191    pub const fn placeholder(&self) -> Option<&PlaceholderFormat> {
192        match self {
193            Self::AutoShape(s) => s.placeholder.as_ref(),
194            Self::Picture(s) => s.placeholder.as_ref(),
195            Self::GraphicFrame(s) => s.placeholder.as_ref(),
196            Self::GroupShape(_) | Self::Connector(_) | Self::OleObject(_) => None,
197        }
198    }
199}
200
201impl WriteXml for Shape {
202    fn write_xml<W: std::fmt::Write>(&self, w: &mut W) -> std::fmt::Result {
203        match self {
204            Self::AutoShape(s) => s.write_xml(w),
205            Self::Picture(s) => s.write_xml(w),
206            Self::GroupShape(s) => s.write_xml(w),
207            Self::Connector(s) => s.write_xml(w),
208            Self::OleObject(s) => s.write_xml(w),
209            Self::GraphicFrame(_) => {
210                // GraphicFrame (tables, charts, SmartArt) is serialized through
211                // specialized paths in ShapeTree that preserve the original XML.
212                // Direct WriteXml serialization is not supported for this shape type.
213                Ok(())
214            }
215        }
216    }
217}
218
219impl fmt::Display for Shape {
220    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221        let kind = match self {
222            Self::AutoShape(_) => "AutoShape",
223            Self::Picture(_) => "Picture",
224            Self::GraphicFrame(_) => "GraphicFrame",
225            Self::GroupShape(_) => "GroupShape",
226            Self::Connector(_) => "Connector",
227            Self::OleObject(_) => "OleObject",
228        };
229        write!(f, "{}(\"{}\")", kind, self.name())
230    }
231}