Skip to main content

shapes_demo/
shapes_demo.rs

1//! Example demonstrating shape creation in PPTX
2//!
3//! Shows various shape types, fills, lines, and text in shapes.
4//! NEW: Demonstrates the flexible Dimension API for positioning and sizing.
5
6use ppt_rs::generator::{
7    Shape, ShapeType, ShapeFill, ShapeLine,
8    generate_shape_xml, generate_shapes_xml, generate_connector_xml,
9    inches_to_emu, cm_to_emu,
10};
11use ppt_rs::core::Dimension;
12
13fn main() {
14    println!("╔════════════════════════════════════════════════════════════╗");
15    println!("║         PPTX Shapes Demo                                   ║");
16    println!("╚════════════════════════════════════════════════════════════╝\n");
17
18    // =========================================================================
19    // Basic Shapes
20    // =========================================================================
21    println!("📐 Basic Shapes:");
22    
23    let basic_shapes = [
24        ShapeType::Rectangle,
25        ShapeType::RoundedRectangle,
26        ShapeType::Ellipse,
27        ShapeType::Triangle,
28        ShapeType::Diamond,
29        ShapeType::Pentagon,
30        ShapeType::Hexagon,
31        ShapeType::Octagon,
32    ];
33    
34    for shape_type in &basic_shapes {
35        println!("   {} → {}", shape_type.display_name(), shape_type.preset_name());
36    }
37
38    // =========================================================================
39    // Arrow Shapes
40    // =========================================================================
41    println!("\n➡️  Arrow Shapes:");
42    
43    let arrow_shapes = [
44        ShapeType::RightArrow,
45        ShapeType::LeftArrow,
46        ShapeType::UpArrow,
47        ShapeType::DownArrow,
48        ShapeType::LeftRightArrow,
49        ShapeType::UpDownArrow,
50    ];
51    
52    for shape_type in &arrow_shapes {
53        println!("   {} → {}", shape_type.display_name(), shape_type.preset_name());
54    }
55
56    // =========================================================================
57    // Star and Banner Shapes
58    // =========================================================================
59    println!("\n⭐ Stars and Banners:");
60    
61    let star_shapes = [
62        ShapeType::Star4,
63        ShapeType::Star5,
64        ShapeType::Star6,
65        ShapeType::Star8,
66        ShapeType::Ribbon,
67        ShapeType::Wave,
68    ];
69    
70    for shape_type in &star_shapes {
71        println!("   {} → {}", shape_type.display_name(), shape_type.preset_name());
72    }
73
74    // =========================================================================
75    // Callout Shapes
76    // =========================================================================
77    println!("\n💬 Callout Shapes:");
78    
79    let callout_shapes = [
80        ShapeType::WedgeRectCallout,
81        ShapeType::WedgeEllipseCallout,
82        ShapeType::CloudCallout,
83    ];
84    
85    for shape_type in &callout_shapes {
86        println!("   {} → {}", shape_type.display_name(), shape_type.preset_name());
87    }
88
89    // =========================================================================
90    // Flow Chart Shapes
91    // =========================================================================
92    println!("\n📊 Flow Chart Shapes:");
93    
94    let flowchart_shapes = [
95        ShapeType::FlowChartProcess,
96        ShapeType::FlowChartDecision,
97        ShapeType::FlowChartTerminator,
98        ShapeType::FlowChartDocument,
99    ];
100    
101    for shape_type in &flowchart_shapes {
102        println!("   {} → {}", shape_type.display_name(), shape_type.preset_name());
103    }
104
105    // =========================================================================
106    // Other Shapes
107    // =========================================================================
108    println!("\n🎨 Other Shapes:");
109    
110    let other_shapes = [
111        ShapeType::Heart,
112        ShapeType::Lightning,
113        ShapeType::Sun,
114        ShapeType::Moon,
115        ShapeType::Cloud,
116    ];
117    
118    for shape_type in &other_shapes {
119        println!("   {} → {}", shape_type.display_name(), shape_type.preset_name());
120    }
121
122    // =========================================================================
123    // Shape with Fill
124    // =========================================================================
125    println!("\n🎨 Shape with Fill:");
126    
127    let filled_shape = Shape::new(
128        ShapeType::Rectangle,
129        inches_to_emu(1.0),
130        inches_to_emu(1.0),
131        inches_to_emu(3.0),
132        inches_to_emu(2.0),
133    ).with_fill(ShapeFill::new("4472C4")); // Blue fill
134    
135    let xml = generate_shape_xml(&filled_shape, 1);
136    println!("   Generated XML ({} chars)", xml.len());
137    println!("   Contains fill: {}", xml.contains("solidFill"));
138
139    // =========================================================================
140    // Shape with Line
141    // =========================================================================
142    println!("\n📏 Shape with Line:");
143    
144    let outlined_shape = Shape::new(
145        ShapeType::Ellipse,
146        inches_to_emu(1.0),
147        inches_to_emu(1.0),
148        inches_to_emu(2.0),
149        inches_to_emu(2.0),
150    ).with_line(ShapeLine::new("FF0000", 25400)); // Red outline, 2pt
151    
152    let xml = generate_shape_xml(&outlined_shape, 2);
153    println!("   Generated XML ({} chars)", xml.len());
154    println!("   Contains line: {}", xml.contains("a:ln"));
155
156    // =========================================================================
157    // Shape with Text
158    // =========================================================================
159    println!("\n📝 Shape with Text:");
160    
161    let text_shape = Shape::new(
162        ShapeType::RoundedRectangle,
163        cm_to_emu(5.0),
164        cm_to_emu(3.0),
165        cm_to_emu(8.0),
166        cm_to_emu(4.0),
167    )
168    .with_fill(ShapeFill::new("70AD47")) // Green fill
169    .with_text("Click Here!");
170    
171    let xml = generate_shape_xml(&text_shape, 3);
172    println!("   Generated XML ({} chars)", xml.len());
173    println!("   Contains text: {}", xml.contains("Click Here!"));
174
175    // =========================================================================
176    // Multiple Shapes
177    // =========================================================================
178    println!("\n📦 Multiple Shapes:");
179    
180    let shapes = vec![
181        Shape::new(ShapeType::Rectangle, 0, 0, 1000000, 500000)
182            .with_fill(ShapeFill::new("FF0000")),
183        Shape::new(ShapeType::Ellipse, 1200000, 0, 500000, 500000)
184            .with_fill(ShapeFill::new("00FF00")),
185        Shape::new(ShapeType::Triangle, 1900000, 0, 500000, 500000)
186            .with_fill(ShapeFill::new("0000FF")),
187    ];
188    
189    let xml = generate_shapes_xml(&shapes, 10);
190    println!("   Generated {} shapes", shapes.len());
191    println!("   Total XML: {} chars", xml.len());
192
193    // =========================================================================
194    // Connector (Arrow Line)
195    // =========================================================================
196    println!("\n🔗 Connector:");
197    
198    let connector_xml = generate_connector_xml(
199        0, 0,
200        inches_to_emu(3.0), inches_to_emu(2.0),
201        100,
202        "000000",
203        12700, // 1pt line
204    );
205    println!("   Generated connector XML ({} chars)", connector_xml.len());
206    println!("   Has arrow head: {}", connector_xml.contains("triangle"));
207
208    // =========================================================================
209    // Flexible Dimension API (NEW)
210    // =========================================================================
211    println!("\n📏 Flexible Dimension API (NEW):");
212
213    // 1. Shape using ratio-based positioning (% of slide)
214    let ratio_shape = Shape::from_dimensions(
215        ShapeType::Rectangle,
216        Dimension::Ratio(0.1), Dimension::Ratio(0.2),   // 10% from left, 20% from top
217        Dimension::Ratio(0.8), Dimension::Ratio(0.6),   // 80% wide, 60% tall
218    ).with_fill(ShapeFill::new("4472C4")).with_text("Ratio-based");
219
220    let xml = generate_shape_xml(&ratio_shape, 20);
221    println!("   Ratio-based shape: {}x{} EMU at ({}, {})",
222        ratio_shape.width, ratio_shape.height, ratio_shape.x, ratio_shape.y);
223    println!("   Generated XML ({} chars)", xml.len());
224
225    // 2. Mixed units: inches for position, ratio for size
226    let mixed_shape = Shape::from_dimensions(
227        ShapeType::RoundedRectangle,
228        Dimension::Inches(1.0), Dimension::Cm(3.0),     // 1 inch from left, 3cm from top
229        Dimension::Ratio(0.5), Dimension::Inches(1.5),  // 50% slide width, 1.5 inches tall
230    ).with_fill(ShapeFill::new("70AD47")).with_text("Mixed units");
231
232    println!("   Mixed-unit shape: {}x{} EMU at ({}, {})",
233        mixed_shape.width, mixed_shape.height, mixed_shape.x, mixed_shape.y);
234
235    // 3. Fluent .at() and .with_dimensions() chaining
236    let fluent_shape = Shape::new(ShapeType::Ellipse, 0, 0, 0, 0)
237        .at(Dimension::percent(50.0), Dimension::percent(50.0))  // center of slide
238        .with_dimensions(Dimension::Inches(2.0), Dimension::Inches(2.0))
239        .with_fill(ShapeFill::new("C0504D"))
240        .with_text("Centered");
241
242    println!("   Fluent chained shape: {}x{} EMU at ({}, {})",
243        fluent_shape.width, fluent_shape.height, fluent_shape.x, fluent_shape.y);
244
245    // 4. Percent helper (syntactic sugar for Ratio)
246    let percent_shape = Shape::from_dimensions(
247        ShapeType::Diamond,
248        Dimension::percent(40.0), Dimension::percent(30.0),
249        Dimension::percent(20.0), Dimension::percent(40.0),
250    ).with_fill(ShapeFill::new("8064A2"));
251
252    println!("   Percent-based shape: {}x{} EMU at ({}, {})",
253        percent_shape.width, percent_shape.height, percent_shape.x, percent_shape.y);
254
255    // 5. Points (useful for font-relative sizing)
256    let pt_shape = Shape::from_dimensions(
257        ShapeType::Rectangle,
258        Dimension::Pt(72.0), Dimension::Pt(72.0),   // 1 inch = 72pt
259        Dimension::Pt(360.0), Dimension::Pt(144.0), // 5 inches x 2 inches
260    ).with_fill(ShapeFill::new("F79646")).with_text("Points");
261
262    println!("   Point-based shape: {}x{} EMU at ({}, {})",
263        pt_shape.width, pt_shape.height, pt_shape.x, pt_shape.y);
264
265    // 6. All Dimension types side by side (same 1-inch result)
266    println!("\n   Unit equivalence (all = 1 inch = 914400 EMU):");
267    let units = [
268        ("Emu(914400)", Dimension::Emu(914400)),
269        ("Inches(1.0)", Dimension::Inches(1.0)),
270        ("Cm(2.54)",    Dimension::Cm(2.54)),
271        ("Pt(72.0)",    Dimension::Pt(72.0)),
272        ("Ratio(0.1)",  Dimension::Ratio(0.1)),  // 10% of slide width (10 inches)
273    ];
274    for (label, dim) in &units {
275        println!("     {:<16} → {} EMU", label, dim.to_emu_x());
276    }
277
278    // =========================================================================
279    // Summary
280    // =========================================================================
281    println!("\n╔════════════════════════════════════════════════════════════╗");
282    println!("║                    Demo Complete                           ║");
283    println!("╠════════════════════════════════════════════════════════════╣");
284    println!("║  Shape Types Available: 40+                                ║");
285    println!("║  Features:                                                 ║");
286    println!("║  ✓ Basic shapes (rect, ellipse, triangle, etc.)            ║");
287    println!("║  ✓ Arrow shapes (8 directions)                             ║");
288    println!("║  ✓ Stars and banners                                       ║");
289    println!("║  ✓ Callouts                                                ║");
290    println!("║  ✓ Flow chart shapes                                       ║");
291    println!("║  ✓ Fill colors with transparency                           ║");
292    println!("║  ✓ Line/border styling                                     ║");
293    println!("║  ✓ Text inside shapes                                      ║");
294    println!("║  ✓ Connectors with arrow heads                             ║");
295    println!("║  ✓ NEW: Flexible Dimension API (EMU/inches/cm/pt/ratio)    ║");
296    println!("║  ✓ NEW: Fluent .at() and .with_dimensions() chaining       ║");
297    println!("║  ✓ NEW: Percent-based positioning                          ║");
298    println!("╚════════════════════════════════════════════════════════════╝");
299}