ppt_rs/helpers/mod.rs
1//! Simplified helper functions for common operations
2//!
3//! This module provides concise, easy-to-use helper functions that make
4//! creating presentations more intuitive and require less boilerplate code.
5//!
6//! # Examples
7//!
8//! ```rust
9//! use ppt_rs::helpers::*;
10//!
11//! // Create shapes with simple helpers
12//! let rect = rect(0.5, 1.0, 2.0, 1.5)
13//! .fill(hex("4F81BD"))
14//! .text("Hello");
15//!
16//! // Create images easily
17//! let bytes = vec![0u8; 100]; // Example image bytes
18//! let img = image(bytes)
19//! .size(914400 * 2, 914400 * 2) // 2 inches in EMUs
20//! .at(914400, 914400); // 1 inch in EMUs
21//! ```
22
23pub mod colors;
24pub mod tables;
25
26use crate::generator::{
27 Shape, ShapeType, ShapeFill, ShapeLine,
28 ImageBuilder,
29 TableBuilder, ChartBuilder, ChartType,
30};
31use crate::elements::{Color, RgbColor};
32use crate::core::Dimension;
33
34// Re-export color utilities
35pub use colors::ColorValue;
36pub use colors::{
37 red, green, blue, yellow, cyan, magenta, white, black, gray, grey,
38 light_gray, light_grey, dark_gray, dark_grey, silver,
39 orange, purple, pink, brown, navy, teal, olive, maroon, lime, aqua,
40 material_red, material_pink, material_purple, material_indigo,
41 material_blue, material_cyan, material_teal, material_green,
42 material_lime, material_amber, material_orange, material_brown,
43 material_gray, material_grey,
44 corporate_blue, corporate_green, corporate_red, corporate_orange,
45};
46
47// Re-export table utilities
48pub use tables::{
49 simple_table, table_with_widths, table_from_data, table_with_header,
50 QuickTable, cell, header_cell, highlight_cell,
51};
52
53// ============================================================================
54// Shape Helpers
55// ============================================================================
56
57/// Create a rectangle shape at the specified position with the given size.
58/// All dimensions are in inches.
59///
60/// # Example
61/// ```
62/// use ppt_rs::helpers::rect;
63///
64/// let shape = rect(0.5, 1.0, 2.0, 1.5);
65/// ```
66pub fn rect(x: f64, y: f64, width: f64, height: f64) -> Shape {
67 Shape::from_dimensions(
68 ShapeType::Rectangle,
69 Dimension::Inches(x),
70 Dimension::Inches(y),
71 Dimension::Inches(width),
72 Dimension::Inches(height),
73 )
74}
75
76/// Create a circle shape at the specified position with the given diameter.
77/// All dimensions are in inches.
78///
79/// # Example
80/// ```
81/// use ppt_rs::helpers::circle;
82///
83/// let shape = circle(1.0, 1.0, 2.0);
84/// ```
85pub fn circle(x: f64, y: f64, diameter: f64) -> Shape {
86 Shape::from_dimensions(
87 ShapeType::Ellipse,
88 Dimension::Inches(x),
89 Dimension::Inches(y),
90 Dimension::Inches(diameter),
91 Dimension::Inches(diameter),
92 )
93}
94
95/// Create an ellipse shape at the specified position with the given size.
96/// All dimensions are in inches.
97///
98/// # Example
99/// ```
100/// use ppt_rs::helpers::ellipse;
101///
102/// let shape = ellipse(1.0, 1.0, 3.0, 2.0);
103/// ```
104pub fn ellipse(x: f64, y: f64, width: f64, height: f64) -> Shape {
105 Shape::from_dimensions(
106 ShapeType::Ellipse,
107 Dimension::Inches(x),
108 Dimension::Inches(y),
109 Dimension::Inches(width),
110 Dimension::Inches(height),
111 )
112}
113
114/// Create a rounded rectangle shape at the specified position with the given size.
115/// All dimensions are in inches.
116///
117/// # Example
118/// ```
119/// use ppt_rs::helpers::rounded_rect;
120///
121/// let shape = rounded_rect(0.5, 1.0, 2.0, 1.5);
122/// ```
123pub fn rounded_rect(x: f64, y: f64, width: f64, height: f64) -> Shape {
124 Shape::from_dimensions(
125 ShapeType::RoundedRectangle,
126 Dimension::Inches(x),
127 Dimension::Inches(y),
128 Dimension::Inches(width),
129 Dimension::Inches(height),
130 )
131}
132
133/// Create a triangle shape at the specified position with the given size.
134/// All dimensions are in inches.
135///
136/// # Example
137/// ```
138/// use ppt_rs::helpers::triangle;
139///
140/// let shape = triangle(1.0, 1.0, 2.0, 1.5);
141/// ```
142pub fn triangle(x: f64, y: f64, width: f64, height: f64) -> Shape {
143 Shape::from_dimensions(
144 ShapeType::Triangle,
145 Dimension::Inches(x),
146 Dimension::Inches(y),
147 Dimension::Inches(width),
148 Dimension::Inches(height),
149 )
150}
151
152/// Create a diamond shape at the specified position with the given size.
153/// All dimensions are in inches.
154///
155/// # Example
156/// ```
157/// use ppt_rs::helpers::diamond;
158///
159/// let shape = diamond(1.0, 1.0, 2.0, 2.0);
160/// ```
161pub fn diamond(x: f64, y: f64, width: f64, height: f64) -> Shape {
162 Shape::from_dimensions(
163 ShapeType::Diamond,
164 Dimension::Inches(x),
165 Dimension::Inches(y),
166 Dimension::Inches(width),
167 Dimension::Inches(height),
168 )
169}
170
171// ============================================================================
172// Image Helpers
173// ============================================================================
174
175/// Create an image from raw bytes with automatic format detection.
176///
177/// # Example
178/// ```no_run
179/// use ppt_rs::helpers::image;
180///
181/// let bytes = std::fs::read("photo.jpg").unwrap();
182/// let img = image(bytes)
183/// .size(914400 * 2, 914400 * 2) // 2 inches in EMUs
184/// .at(914400, 914400); // 1 inch in EMUs
185/// ```
186pub fn image<T: Into<Vec<u8>>>(data: T) -> ImageBuilder {
187 ImageBuilder::auto(data.into())
188}
189
190/// Create an image from a file path.
191///
192/// # Example
193/// ```no_run
194/// use ppt_rs::helpers::image_file;
195///
196/// let img = image_file("photo.jpg").unwrap()
197/// .size(914400 * 2, 914400 * 2) // 2 inches in EMUs
198/// .at(914400, 914400); // 1 inch in EMUs
199/// ```
200pub fn image_file(path: &str) -> crate::exc::Result<ImageBuilder> {
201 let bytes = std::fs::read(path)?;
202 Ok(ImageBuilder::auto(bytes))
203}
204
205// ============================================================================
206// Color Helpers
207// ============================================================================
208
209/// Create an RGB color from red, green, and blue components (0-255).
210///
211/// # Example
212/// ```
213/// use ppt_rs::helpers::rgb;
214///
215/// let color = rgb(79, 129, 189); // Blue
216/// ```
217pub fn rgb(r: u8, g: u8, b: u8) -> Color {
218 Color::Rgb(RgbColor::new(r, g, b))
219}
220
221/// Create a color from a hex string (with or without '#' prefix).
222///
223/// # Example
224/// ```
225/// use ppt_rs::helpers::hex;
226///
227/// let color1 = hex("4F81BD");
228/// let color2 = hex("#4F81BD"); // Also works
229/// ```
230pub fn hex(color: &str) -> Color {
231 let color = color.trim_start_matches('#');
232 if color.len() == 6 {
233 let r = u8::from_str_radix(&color[0..2], 16).unwrap_or(0);
234 let g = u8::from_str_radix(&color[2..4], 16).unwrap_or(0);
235 let b = u8::from_str_radix(&color[4..6], 16).unwrap_or(0);
236 Color::Rgb(RgbColor::new(r, g, b))
237 } else {
238 Color::Rgb(RgbColor::new(0, 0, 0)) // Default to black
239 }
240}
241
242// ============================================================================
243// Table Helpers
244// ============================================================================
245
246/// Create a table builder with the specified column widths.
247///
248/// # Example
249/// ```
250/// use ppt_rs::helpers::table;
251///
252/// let tbl = table(vec![2000000, 2000000, 2000000]); // 3 columns with equal widths
253/// ```
254pub fn table(column_widths: Vec<u32>) -> TableBuilder {
255 TableBuilder::new(column_widths)
256}
257
258// ============================================================================
259// Chart Helpers
260// ============================================================================
261
262/// Create a bar chart builder.
263///
264/// # Example
265/// ```
266/// use ppt_rs::helpers::bar_chart;
267///
268/// let chart = bar_chart("Sales Data");
269/// ```
270pub fn bar_chart(title: &str) -> ChartBuilder {
271 ChartBuilder::new(title, ChartType::Bar)
272}
273
274/// Create a line chart builder.
275///
276/// # Example
277/// ```
278/// use ppt_rs::helpers::line_chart;
279///
280/// let chart = line_chart("Trends");
281/// ```
282pub fn line_chart(title: &str) -> ChartBuilder {
283 ChartBuilder::new(title, ChartType::Line)
284}
285
286/// Create a pie chart builder.
287///
288/// # Example
289/// ```
290/// use ppt_rs::helpers::pie_chart;
291///
292/// let chart = pie_chart("Distribution");
293/// ```
294pub fn pie_chart(title: &str) -> ChartBuilder {
295 ChartBuilder::new(title, ChartType::Pie)
296}
297
298/// Create an area chart builder.
299///
300/// # Example
301/// ```
302/// use ppt_rs::helpers::area_chart;
303///
304/// let chart = area_chart("Growth");
305/// ```
306pub fn area_chart(title: &str) -> ChartBuilder {
307 ChartBuilder::new(title, ChartType::Area)
308}
309
310// ============================================================================
311// Extension Traits for Fluent API
312// ============================================================================
313
314/// Extension trait for shapes to provide shorter method names
315pub trait ShapeExt {
316 /// Set the fill color (shorter alias for `with_fill`)
317 fn fill(self, color: Color) -> Self;
318
319 /// Set the stroke/line (shorter alias for `with_line`)
320 fn stroke(self, color: Color, width_pt: f64) -> Self;
321
322 /// Set the text (shorter alias for `with_text`)
323 fn text(self, text: &str) -> Self;
324}
325
326impl ShapeExt for Shape {
327 fn fill(self, color: Color) -> Self {
328 let color_str = match color {
329 Color::Rgb(rgb) => format!("{:02X}{:02X}{:02X}", rgb.r, rgb.g, rgb.b),
330 _ => "000000".to_string(),
331 };
332 self.with_fill(ShapeFill::new(&color_str))
333 }
334
335 fn stroke(self, color: Color, width_pt: f64) -> Self {
336 let color_str = match color {
337 Color::Rgb(rgb) => format!("{:02X}{:02X}{:02X}", rgb.r, rgb.g, rgb.b),
338 _ => "000000".to_string(),
339 };
340 let width_emu = (width_pt * 12700.0) as u32; // Convert points to EMU
341 self.with_line(ShapeLine::new(&color_str, width_emu))
342 }
343
344 fn text(self, text: &str) -> Self {
345 self.with_text(text)
346 }
347}
348
349#[cfg(test)]
350mod tests {
351 use super::*;
352 use crate::core::{Positioned, ElementSized};
353
354 #[test]
355 fn test_rect_helper() {
356 let shape = rect(0.5, 1.0, 2.0, 1.5);
357 assert_eq!(shape.x(), (0.5 * 914400.0) as u32);
358 assert_eq!(shape.y(), (1.0 * 914400.0) as u32);
359 }
360
361 #[test]
362 fn test_circle_helper() {
363 let shape = circle(2.0, 3.0, 1.5);
364 assert_eq!(shape.width(), shape.height()); // Circle is square
365 }
366
367 #[test]
368 fn test_rgb_helper() {
369 let color = rgb(79, 129, 189);
370 match color {
371 Color::Rgb(rgb) => {
372 assert_eq!(rgb.r, 79);
373 assert_eq!(rgb.g, 129);
374 assert_eq!(rgb.b, 189);
375 }
376 _ => panic!("Expected RGB color"),
377 }
378 }
379
380 #[test]
381 fn test_hex_helper() {
382 let color1 = hex("4F81BD");
383 let color2 = hex("#4F81BD");
384
385 match (color1, color2) {
386 (Color::Rgb(rgb1), Color::Rgb(rgb2)) => {
387 assert_eq!(rgb1.r, 79);
388 assert_eq!(rgb1.g, 129);
389 assert_eq!(rgb1.b, 189);
390 assert_eq!(rgb1.r, rgb2.r);
391 assert_eq!(rgb1.g, rgb2.g);
392 assert_eq!(rgb1.b, rgb2.b);
393 }
394 _ => panic!("Expected RGB colors"),
395 }
396 }
397
398 #[test]
399 fn test_shape_ext() {
400 let shape = rect(0.5, 1.0, 2.0, 1.5)
401 .fill(rgb(79, 129, 189))
402 .text("Hello");
403
404 assert!(shape.text.is_some());
405 assert_eq!(shape.text.unwrap(), "Hello");
406 }
407}