ppt_rs/
prelude.rs

1//! Prelude module for easy imports
2//!
3//! This module provides a simplified API for common use cases.
4//!
5//! # Quick Start
6//!
7//! ```rust,ignore
8//! use ppt_rs::prelude::*;
9//!
10//! // Create a simple presentation
11//! let pptx = pptx!("My Presentation")
12//!     .slide("Welcome", &["Point 1", "Point 2"])
13//!     .slide("Details", &["More info"])
14//!     .build()
15//!     .unwrap();
16//!
17//! std::fs::write("output.pptx", pptx).unwrap();
18//! ```
19
20// Re-export commonly used types
21pub use crate::generator::{
22    SlideContent, SlideLayout,
23    Shape, ShapeType, ShapeFill, ShapeLine,
24    Image,
25    Connector, ConnectorType, ArrowType,
26    create_pptx, create_pptx_with_content,
27};
28
29pub use crate::generator::shapes::{
30    GradientFill, GradientDirection, GradientStop,
31};
32
33pub use crate::elements::{Color, RgbColor, Position, Size};
34pub use crate::exc::Result;
35
36/// Quick presentation builder macro
37#[macro_export]
38macro_rules! pptx {
39    ($title:expr) => {
40        $crate::prelude::QuickPptx::new($title)
41    };
42}
43
44/// Quick shape creation
45#[macro_export]
46macro_rules! shape {
47    // Rectangle with position and size (in inches)
48    (rect $x:expr, $y:expr, $w:expr, $h:expr) => {
49        $crate::prelude::Shape::new(
50            $crate::prelude::ShapeType::Rectangle,
51            $crate::prelude::inches($x),
52            $crate::prelude::inches($y),
53            $crate::prelude::inches($w),
54            $crate::prelude::inches($h),
55        )
56    };
57    // Circle with position and size (in inches)
58    (circle $x:expr, $y:expr, $size:expr) => {
59        $crate::prelude::Shape::new(
60            $crate::prelude::ShapeType::Circle,
61            $crate::prelude::inches($x),
62            $crate::prelude::inches($y),
63            $crate::prelude::inches($size),
64            $crate::prelude::inches($size),
65        )
66    };
67}
68
69/// Convert inches to EMU (English Metric Units)
70pub fn inches(val: f64) -> u32 {
71    (val * 914400.0) as u32
72}
73
74/// Convert centimeters to EMU
75pub fn cm(val: f64) -> u32 {
76    (val * 360000.0) as u32
77}
78
79/// Convert points to EMU
80pub fn pt(val: f64) -> u32 {
81    (val * 12700.0) as u32
82}
83
84/// Quick presentation builder for simple use cases
85pub struct QuickPptx {
86    title: String,
87    slides: Vec<SlideContent>,
88}
89
90impl QuickPptx {
91    /// Create a new presentation with a title
92    pub fn new(title: &str) -> Self {
93        QuickPptx {
94            title: title.to_string(),
95            slides: Vec::new(),
96        }
97    }
98    
99    /// Add a slide with title and bullet points
100    pub fn slide(mut self, title: &str, bullets: &[&str]) -> Self {
101        let mut slide = SlideContent::new(title);
102        for bullet in bullets {
103            slide = slide.add_bullet(*bullet);
104        }
105        self.slides.push(slide);
106        self
107    }
108    
109    /// Add a slide with just a title
110    pub fn title_slide(mut self, title: &str) -> Self {
111        self.slides.push(SlideContent::new(title));
112        self
113    }
114    
115    /// Add a slide with title and custom content
116    pub fn content_slide(mut self, slide: SlideContent) -> Self {
117        self.slides.push(slide);
118        self
119    }
120    
121    /// Add a slide with shapes
122    pub fn shapes_slide(mut self, title: &str, shapes: Vec<Shape>) -> Self {
123        let slide = SlideContent::new(title).with_shapes(shapes);
124        self.slides.push(slide);
125        self
126    }
127    
128    /// Build the presentation and return the PPTX data
129    pub fn build(self) -> std::result::Result<Vec<u8>, Box<dyn std::error::Error>> {
130        if self.slides.is_empty() {
131            // Create at least one slide
132            create_pptx(&self.title, 1)
133        } else {
134            create_pptx_with_content(&self.title, self.slides)
135        }
136    }
137    
138    /// Build and save to a file
139    pub fn save(self, path: &str) -> std::result::Result<(), Box<dyn std::error::Error>> {
140        let data = self.build()?;
141        std::fs::write(path, data)?;
142        Ok(())
143    }
144}
145
146
147/// Quick shape builders
148pub mod shapes {
149    use super::*;
150    
151    /// Create a rectangle
152    pub fn rect(x: f64, y: f64, width: f64, height: f64) -> Shape {
153        Shape::new(ShapeType::Rectangle, inches(x), inches(y), inches(width), inches(height))
154    }
155    
156    /// Create a circle
157    pub fn circle(x: f64, y: f64, diameter: f64) -> Shape {
158        Shape::new(ShapeType::Circle, inches(x), inches(y), inches(diameter), inches(diameter))
159    }
160    
161    /// Create a rounded rectangle
162    pub fn rounded_rect(x: f64, y: f64, width: f64, height: f64) -> Shape {
163        Shape::new(ShapeType::RoundedRectangle, inches(x), inches(y), inches(width), inches(height))
164    }
165    
166    /// Create a text box (rectangle with text)
167    pub fn text_box(x: f64, y: f64, width: f64, height: f64, text: &str) -> Shape {
168        Shape::new(ShapeType::Rectangle, inches(x), inches(y), inches(width), inches(height))
169            .with_text(text)
170    }
171    
172    /// Create a colored shape
173    pub fn colored(shape: Shape, fill: &str, line: Option<&str>) -> Shape {
174        let mut s = shape.with_fill(ShapeFill::new(fill));
175        if let Some(l) = line {
176            s = s.with_line(ShapeLine::new(l, 12700));
177        }
178        s
179    }
180    
181    /// Create a gradient shape
182    pub fn gradient(shape: Shape, start: &str, end: &str, direction: GradientDirection) -> Shape {
183        shape.with_gradient(GradientFill::linear(start, end, direction))
184    }
185}
186
187/// Color constants for convenience
188pub mod colors {
189    pub const RED: &str = "FF0000";
190    pub const GREEN: &str = "00FF00";
191    pub const BLUE: &str = "0000FF";
192    pub const WHITE: &str = "FFFFFF";
193    pub const BLACK: &str = "000000";
194    pub const GRAY: &str = "808080";
195    pub const LIGHT_GRAY: &str = "D3D3D3";
196    pub const DARK_GRAY: &str = "404040";
197    pub const YELLOW: &str = "FFFF00";
198    pub const ORANGE: &str = "FFA500";
199    pub const PURPLE: &str = "800080";
200    pub const CYAN: &str = "00FFFF";
201    pub const MAGENTA: &str = "FF00FF";
202    pub const NAVY: &str = "000080";
203    pub const TEAL: &str = "008080";
204    pub const OLIVE: &str = "808000";
205    
206    // Corporate colors
207    pub const CORPORATE_BLUE: &str = "1565C0";
208    pub const CORPORATE_GREEN: &str = "2E7D32";
209    pub const CORPORATE_RED: &str = "C62828";
210    pub const CORPORATE_ORANGE: &str = "EF6C00";
211}
212
213#[cfg(test)]
214mod tests {
215    use super::*;
216    
217    #[test]
218    fn test_quick_pptx() {
219        let result = QuickPptx::new("Test")
220            .slide("Slide 1", &["Point 1", "Point 2"])
221            .build();
222        assert!(result.is_ok());
223    }
224    
225    #[test]
226    fn test_inches_conversion() {
227        assert_eq!(inches(1.0), 914400);
228        assert_eq!(cm(2.54), 914400); // 1 inch = 2.54 cm
229    }
230    
231    #[test]
232    fn test_shape_builders() {
233        let rect = shapes::rect(1.0, 1.0, 2.0, 1.0);
234        assert_eq!(rect.width, inches(2.0));
235        
236        let circle = shapes::circle(1.0, 1.0, 1.0);
237        assert_eq!(circle.width, circle.height);
238    }
239}