use std::fmt;
use super::super::package::PptError;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ShapeType {
TextBox,
Placeholder,
AutoShape,
Picture,
Group,
Line,
Connector,
Object,
Table,
Unknown(u16),
}
impl From<u16> for ShapeType {
fn from(value: u16) -> Self {
match value {
1 => ShapeType::TextBox,
2 => ShapeType::Placeholder,
3 => ShapeType::AutoShape,
4 => ShapeType::Picture,
5 => ShapeType::Group,
6 => ShapeType::Line,
7 => ShapeType::Connector,
8 => ShapeType::Object,
9 => ShapeType::Table,
other => ShapeType::Unknown(other),
}
}
}
impl From<crate::ole::consts::EscherShapeType> for ShapeType {
fn from(escher_type: crate::ole::consts::EscherShapeType) -> Self {
match escher_type {
crate::ole::consts::EscherShapeType::NotPrimitive => ShapeType::Unknown(0),
crate::ole::consts::EscherShapeType::Rectangle => ShapeType::AutoShape,
crate::ole::consts::EscherShapeType::RoundRectangle => ShapeType::AutoShape,
crate::ole::consts::EscherShapeType::Oval => ShapeType::AutoShape,
crate::ole::consts::EscherShapeType::Diamond => ShapeType::AutoShape,
crate::ole::consts::EscherShapeType::Triangle => ShapeType::AutoShape,
crate::ole::consts::EscherShapeType::RightTriangle => ShapeType::AutoShape,
crate::ole::consts::EscherShapeType::Parallelogram => ShapeType::AutoShape,
crate::ole::consts::EscherShapeType::Trapezoid => ShapeType::AutoShape,
crate::ole::consts::EscherShapeType::Hexagon => ShapeType::AutoShape,
crate::ole::consts::EscherShapeType::Octagon => ShapeType::AutoShape,
crate::ole::consts::EscherShapeType::Plus => ShapeType::AutoShape,
crate::ole::consts::EscherShapeType::Star => ShapeType::AutoShape,
crate::ole::consts::EscherShapeType::Arrow => ShapeType::AutoShape,
crate::ole::consts::EscherShapeType::ThickArrow => ShapeType::AutoShape,
crate::ole::consts::EscherShapeType::HomePlate => ShapeType::AutoShape,
crate::ole::consts::EscherShapeType::Cube => ShapeType::AutoShape,
crate::ole::consts::EscherShapeType::Balloon => ShapeType::AutoShape,
crate::ole::consts::EscherShapeType::Seal => ShapeType::AutoShape,
crate::ole::consts::EscherShapeType::Arc => ShapeType::AutoShape,
crate::ole::consts::EscherShapeType::Line => ShapeType::Line,
crate::ole::consts::EscherShapeType::Plaque => ShapeType::AutoShape,
crate::ole::consts::EscherShapeType::Can => ShapeType::AutoShape,
crate::ole::consts::EscherShapeType::Donut => ShapeType::AutoShape,
crate::ole::consts::EscherShapeType::TextSimple => ShapeType::TextBox,
crate::ole::consts::EscherShapeType::TextOctagon => ShapeType::TextBox,
crate::ole::consts::EscherShapeType::TextHexagon => ShapeType::TextBox,
crate::ole::consts::EscherShapeType::TextCurve => ShapeType::TextBox,
crate::ole::consts::EscherShapeType::TextWave => ShapeType::TextBox,
crate::ole::consts::EscherShapeType::TextRing => ShapeType::TextBox,
crate::ole::consts::EscherShapeType::TextOnCurve => ShapeType::TextBox,
crate::ole::consts::EscherShapeType::TextOnRing => ShapeType::TextBox,
crate::ole::consts::EscherShapeType::StraightConnector1 => ShapeType::Connector,
crate::ole::consts::EscherShapeType::BentConnector2 => ShapeType::Connector,
crate::ole::consts::EscherShapeType::BentConnector3 => ShapeType::Connector,
crate::ole::consts::EscherShapeType::BentConnector4 => ShapeType::Connector,
crate::ole::consts::EscherShapeType::BentConnector5 => ShapeType::Connector,
crate::ole::consts::EscherShapeType::CurvedConnector2 => ShapeType::Connector,
crate::ole::consts::EscherShapeType::CurvedConnector3 => ShapeType::Connector,
crate::ole::consts::EscherShapeType::CurvedConnector4 => ShapeType::Connector,
crate::ole::consts::EscherShapeType::CurvedConnector5 => ShapeType::Connector,
crate::ole::consts::EscherShapeType::Callout1 => ShapeType::AutoShape,
crate::ole::consts::EscherShapeType::Callout2 => ShapeType::AutoShape,
crate::ole::consts::EscherShapeType::Callout3 => ShapeType::AutoShape,
crate::ole::consts::EscherShapeType::AccentCallout1 => ShapeType::AutoShape,
crate::ole::consts::EscherShapeType::AccentCallout2 => ShapeType::AutoShape,
crate::ole::consts::EscherShapeType::AccentCallout3 => ShapeType::AutoShape,
crate::ole::consts::EscherShapeType::BorderCallout1 => ShapeType::AutoShape,
crate::ole::consts::EscherShapeType::BorderCallout2 => ShapeType::AutoShape,
crate::ole::consts::EscherShapeType::BorderCallout3 => ShapeType::AutoShape,
crate::ole::consts::EscherShapeType::AccentBorderCallout1 => ShapeType::AutoShape,
crate::ole::consts::EscherShapeType::AccentBorderCallout2 => ShapeType::AutoShape,
crate::ole::consts::EscherShapeType::AccentBorderCallout3 => ShapeType::AutoShape,
crate::ole::consts::EscherShapeType::Custom => ShapeType::AutoShape,
}
}
}
impl fmt::Display for ShapeType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ShapeType::TextBox => write!(f, "TextBox"),
ShapeType::Placeholder => write!(f, "Placeholder"),
ShapeType::AutoShape => write!(f, "AutoShape"),
ShapeType::Picture => write!(f, "Picture"),
ShapeType::Group => write!(f, "Group"),
ShapeType::Line => write!(f, "Line"),
ShapeType::Connector => write!(f, "Connector"),
ShapeType::Object => write!(f, "Object"),
ShapeType::Table => write!(f, "Table"),
ShapeType::Unknown(id) => write!(f, "Unknown({})", id),
}
}
}
#[derive(Debug, Clone)]
pub struct ShapeProperties {
pub id: u32,
pub shape_type: ShapeType,
pub x: i32,
pub y: i32,
pub width: i32,
pub height: i32,
pub rotation: u16,
pub fill_color: Option<u32>,
pub line_color: Option<u32>,
pub line_width: Option<u16>,
pub hidden: bool,
pub z_order: u16,
}
impl Default for ShapeProperties {
fn default() -> Self {
Self {
id: 0,
shape_type: ShapeType::Unknown(0),
x: 0,
y: 0,
width: 0,
height: 0,
rotation: 0,
fill_color: None,
line_color: None,
line_width: None,
hidden: false,
z_order: 0,
}
}
}
pub trait Shape: std::any::Any {
fn properties(&self) -> &ShapeProperties;
fn properties_mut(&mut self) -> &mut ShapeProperties;
fn shape_type(&self) -> ShapeType {
self.properties().shape_type
}
fn id(&self) -> u32 {
self.properties().id
}
fn text(&self) -> Result<String, PptError>;
fn bounds(&self) -> (i32, i32, i32, i32) {
let props = self.properties();
(props.x, props.y, props.width, props.height)
}
fn is_placeholder(&self) -> bool {
matches!(self.shape_type(), ShapeType::Placeholder)
}
fn has_text(&self) -> bool;
fn clone_box(&self) -> Box<dyn Shape>;
fn as_any(&self) -> &dyn std::any::Any;
}
impl Clone for Box<dyn Shape> {
fn clone(&self) -> Self {
self.clone_box()
}
}
#[derive(Clone)]
pub struct ShapeContainer {
pub properties: ShapeProperties,
pub raw_data: Vec<u8>,
pub text_content: Option<String>,
pub children: Vec<Box<dyn Shape>>,
}
impl std::fmt::Debug for ShapeContainer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ShapeContainer")
.field("properties", &self.properties)
.field("raw_data", &self.raw_data.len())
.field("text_content", &self.text_content)
.field("children", &self.children.len())
.finish()
}
}
impl ShapeContainer {
pub fn new(properties: ShapeProperties, raw_data: Vec<u8>) -> Self {
Self {
properties,
raw_data,
text_content: None,
children: Vec::new(),
}
}
pub fn add_child(&mut self, shape: Box<dyn Shape>) {
self.children.push(shape);
}
pub fn children(&self) -> &[Box<dyn Shape>] {
&self.children
}
pub fn set_text(&mut self, text: String) {
self.text_content = Some(text);
}
}
impl Shape for ShapeContainer {
fn properties(&self) -> &ShapeProperties {
&self.properties
}
fn properties_mut(&mut self) -> &mut ShapeProperties {
&mut self.properties
}
fn text(&self) -> Result<String, PptError> {
Ok(self.text_content.clone().unwrap_or_default())
}
fn has_text(&self) -> bool {
self.text_content.is_some()
}
fn clone_box(&self) -> Box<dyn Shape> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}