Skip to main content

ShapeLine

Struct ShapeLine 

Source
pub struct ShapeLine {
    pub color: String,
    pub width: u32,
}
Expand description

Shape line/border properties

Fields§

§color: String§width: u32

Implementations§

Source§

impl ShapeLine

Source

pub fn new(color: &str, width: u32) -> Self

Create new shape line with color and width

Examples found in repository?
examples/shapes_demo.rs (line 150)
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}
More examples
Hide additional examples
examples/dimension_demo.rs (line 190)
21fn main() -> Result<(), Box<dyn std::error::Error>> {
22    println!("╔══════════════════════════════════════════════════════════════╗");
23    println!("║       Dimension API Demo — Flexible Positioning & Sizing     ║");
24    println!("╚══════════════════════════════════════════════════════════════╝\n");
25
26    let mut slides = Vec::new();
27
28    // =========================================================================
29    // SLIDE 1: Title
30    // =========================================================================
31    slides.push(
32        SlideContent::new("Dimension API — Flexible Positioning & Sizing")
33            .layout(SlideLayout::CenteredTitle)
34            .title_size(44)
35            .title_bold(true)
36            .title_color("1F497D")
37    );
38
39    // =========================================================================
40    // SLIDE 2: All Unit Types Side-by-Side
41    // =========================================================================
42    println!("📏 Slide 2: All Unit Types");
43
44    // Each shape is 1 inch wide, positioned using a different unit type
45    let emu_shape = Shape::from_dimensions(ShapeType::Rectangle,
46        Dimension::Emu(457200), Dimension::Inches(1.5),
47        Dimension::Emu(1371600), Dimension::Inches(0.8),
48    ).with_fill(ShapeFill::new("1565C0")).with_text("EMU");
49
50    let inch_shape = Shape::from_dimensions(ShapeType::Rectangle,
51        Dimension::Inches(2.0), Dimension::Inches(1.5),
52        Dimension::Inches(1.5), Dimension::Inches(0.8),
53    ).with_fill(ShapeFill::new("2E7D32")).with_text("Inches");
54
55    let cm_shape = Shape::from_dimensions(ShapeType::Rectangle,
56        Dimension::Cm(9.0), Dimension::Inches(1.5),
57        Dimension::Cm(3.81), Dimension::Inches(0.8),
58    ).with_fill(ShapeFill::new("C62828")).with_text("Cm");
59
60    let pt_shape = Shape::from_dimensions(ShapeType::Rectangle,
61        Dimension::Pt(324.0), Dimension::Inches(1.5),
62        Dimension::Pt(108.0), Dimension::Inches(0.8),
63    ).with_fill(ShapeFill::new("7B1FA2")).with_text("Pt");
64
65    let ratio_shape = Shape::from_dimensions(ShapeType::Rectangle,
66        Dimension::Ratio(0.52), Dimension::Inches(1.5),
67        Dimension::Ratio(0.15), Dimension::Inches(0.8),
68    ).with_fill(ShapeFill::new("EF6C00")).with_text("Ratio");
69
70    let pct_shape = Shape::from_dimensions(ShapeType::Rectangle,
71        Dimension::percent(69.0), Dimension::Inches(1.5),
72        Dimension::percent(15.0), Dimension::Inches(0.8),
73    ).with_fill(ShapeFill::new("00838F")).with_text("Percent");
74
75    // Labels row
76    let label = Shape::from_dimensions(ShapeType::Rectangle,
77        Dimension::Inches(0.5), Dimension::Inches(0.8),
78        Dimension::Inches(9.0), Dimension::Inches(0.5),
79    ).with_text("Each shape below uses a different unit type for X position:");
80
81    slides.push(
82        SlideContent::new("All Dimension Unit Types")
83            .layout(SlideLayout::TitleOnly)
84            .title_color("1F497D").title_bold(true)
85            .add_shape(label)
86            .add_shape(emu_shape)
87            .add_shape(inch_shape)
88            .add_shape(cm_shape)
89            .add_shape(pt_shape)
90            .add_shape(ratio_shape)
91            .add_shape(pct_shape)
92    );
93
94    // =========================================================================
95    // SLIDE 3: Ratio-Based Grid Layout
96    // =========================================================================
97    println!("📐 Slide 3: Ratio-Based Grid (auto-adapts to slide size)");
98
99    let margin = 0.03;  // 3% margin
100    let gap = 0.02;     // 2% gap
101    let cell_w = (1.0 - 2.0 * margin - 2.0 * gap) / 3.0;
102    let cell_h = (0.7 - 2.0 * gap) / 3.0;  // 70% of slide height for grid
103    let y_start = 0.22; // below title
104
105    let colors = [
106        "1565C0", "2E7D32", "C62828",
107        "7B1FA2", "EF6C00", "00838F",
108        "AD1457", "4E342E", "37474F",
109    ];
110    let labels = [
111        "Top-Left", "Top-Center", "Top-Right",
112        "Mid-Left", "Mid-Center", "Mid-Right",
113        "Bot-Left", "Bot-Center", "Bot-Right",
114    ];
115
116    let mut grid_slide = SlideContent::new("Ratio-Based 3x3 Grid Layout")
117        .layout(SlideLayout::TitleOnly)
118        .title_color("1F497D").title_bold(true);
119
120    for row in 0..3 {
121        for col in 0..3 {
122            let idx = row * 3 + col;
123            let x = margin + col as f64 * (cell_w + gap);
124            let y = y_start + row as f64 * (cell_h + gap);
125            let shape = Shape::from_dimensions(ShapeType::RoundedRectangle,
126                Dimension::Ratio(x), Dimension::Ratio(y),
127                Dimension::Ratio(cell_w), Dimension::Ratio(cell_h),
128            ).with_fill(ShapeFill::new(colors[idx])).with_text(labels[idx]);
129            grid_slide = grid_slide.add_shape(shape);
130        }
131    }
132
133    slides.push(grid_slide);
134
135    // =========================================================================
136    // SLIDE 4: Mixed-Unit Positioning
137    // =========================================================================
138    println!("🔀 Slide 4: Mixed-Unit Positioning");
139
140    // Title area: inches for position, ratio for width
141    let title_box = Shape::from_dimensions(ShapeType::RoundedRectangle,
142        Dimension::Inches(0.5), Dimension::Inches(1.5),
143        Dimension::Ratio(0.9), Dimension::Cm(2.0),
144    ).with_fill(ShapeFill::new("1F497D")).with_text("Inches X + Ratio Width + Cm Height");
145
146    // Content area: cm for position, pt for size
147    let content_box = Shape::from_dimensions(ShapeType::Rectangle,
148        Dimension::Cm(2.0), Dimension::Cm(6.0),
149        Dimension::Pt(432.0), Dimension::Pt(108.0),  // 6in x 1.5in
150    ).with_fill(ShapeFill::new("2E7D32")).with_text("Cm position + Pt size");
151
152    // Footer area: percent for everything
153    let footer_box = Shape::from_dimensions(ShapeType::Rectangle,
154        Dimension::percent(5.0), Dimension::percent(75.0),
155        Dimension::percent(90.0), Dimension::percent(10.0),
156    ).with_fill(ShapeFill::new("C62828")).with_text("100% percent-based");
157
158    // Sidebar: EMU for position, inches for size
159    let sidebar = Shape::from_dimensions(ShapeType::Rectangle,
160        Dimension::Emu(8000000), Dimension::Inches(1.5),
161        Dimension::Inches(1.0), Dimension::Ratio(0.6),
162    ).with_fill(ShapeFill::new("7B1FA2")).with_text("EMU + Inches + Ratio");
163
164    slides.push(
165        SlideContent::new("Mixed-Unit Positioning")
166            .layout(SlideLayout::TitleOnly)
167            .title_color("1F497D").title_bold(true)
168            .add_shape(title_box)
169            .add_shape(content_box)
170            .add_shape(footer_box)
171            .add_shape(sidebar)
172    );
173
174    // =========================================================================
175    // SLIDE 5: Fluent .at() and .with_dimensions() Chaining
176    // =========================================================================
177    println!("🔗 Slide 5: Fluent Chaining API");
178
179    // Build shapes step by step with chaining
180    let shape1 = Shape::new(ShapeType::Ellipse, 0, 0, 0, 0)
181        .at(Dimension::percent(10.0), Dimension::percent(25.0))
182        .with_dimensions(Dimension::Inches(2.5), Dimension::Inches(2.5))
183        .with_fill(ShapeFill::new("1565C0"))
184        .with_text(".at() + .with_dimensions()");
185
186    let shape2 = Shape::new(ShapeType::RoundedRectangle, 0, 0, 0, 0)
187        .at(Dimension::Inches(4.0), Dimension::Cm(5.0))
188        .with_dimensions(Dimension::Ratio(0.3), Dimension::Inches(2.0))
189        .with_fill(ShapeFill::new("2E7D32"))
190        .with_line(ShapeLine::new("1B5E20", 25400))
191        .with_text("Chained with fill + line");
192
193    let shape3 = Shape::new(ShapeType::Star5, 0, 0, 0, 0)
194        .at(Dimension::percent(70.0), Dimension::percent(55.0))
195        .with_dimensions(Dimension::Inches(2.0), Dimension::Inches(2.0))
196        .with_fill(ShapeFill::new("FFC107"))
197        .with_rotation(15)
198        .with_text("+ rotation");
199
200    slides.push(
201        SlideContent::new("Fluent .at() and .with_dimensions() Chaining")
202            .layout(SlideLayout::TitleOnly)
203            .title_color("1F497D").title_bold(true)
204            .add_shape(shape1)
205            .add_shape(shape2)
206            .add_shape(shape3)
207    );
208
209    // =========================================================================
210    // SLIDE 6: Prelude Shape Builders
211    // =========================================================================
212    println!("🧰 Slide 6: Prelude Shape Builders");
213
214    // shapes::dim() — generic Dimension-based builder
215    let dim_shape = shapes::dim(ShapeType::Diamond,
216        Dimension::percent(5.0), Dimension::percent(25.0),
217        Dimension::percent(25.0), Dimension::percent(35.0),
218    ).with_fill(ShapeFill::new("7B1FA2")).with_text("shapes::dim()");
219
220    // shapes::rect_ratio() — ratio-based rectangle
221    let ratio_rect = shapes::rect_ratio(0.35, 0.25, 0.28, 0.35)
222        .with_fill(ShapeFill::new("EF6C00")).with_text("shapes::rect_ratio()");
223
224    // shapes::text_box_ratio() — ratio-based text box
225    let ratio_text = shapes::text_box_ratio(0.68, 0.25, 0.28, 0.35, "shapes::text_box_ratio()")
226        .with_fill(ShapeFill::new("00838F"));
227
228    // Traditional shapes::rect() still works (inches)
229    let inch_rect = shapes::rect(1.0, 5.0, 3.0, 1.0)
230        .with_fill(ShapeFill::new("A5A5A5")).with_text("shapes::rect() (inches)");
231
232    slides.push(
233        SlideContent::new("Prelude Shape Builders")
234            .layout(SlideLayout::TitleOnly)
235            .title_color("1F497D").title_bold(true)
236            .add_shape(dim_shape)
237            .add_shape(ratio_rect)
238            .add_shape(ratio_text)
239            .add_shape(inch_rect)
240    );
241
242    // =========================================================================
243    // SLIDE 7: FlexPosition & FlexSize Structs
244    // =========================================================================
245    println!("📦 Slide 7: FlexPosition & FlexSize");
246
247    // Demonstrate FlexPosition and FlexSize for reusable layout definitions
248    let header_pos = FlexPosition::new(Dimension::percent(5.0), Dimension::percent(20.0));
249    let header_size = FlexSize::new(Dimension::percent(90.0), Dimension::percent(12.0));
250    let (hx, hy) = header_pos.to_emu();
251    let (hw, hh) = header_size.to_emu();
252    let header = Shape::new(ShapeType::RoundedRectangle, hx, hy, hw, hh)
253        .with_fill(ShapeFill::new("1F497D"))
254        .with_text("FlexPosition + FlexSize → header");
255
256    let body_pos = FlexPosition::new(Dimension::percent(5.0), Dimension::percent(35.0));
257    let body_size = FlexSize::new(Dimension::percent(60.0), Dimension::percent(50.0));
258    let (bx, by) = body_pos.to_emu();
259    let (bw, bh) = body_size.to_emu();
260    let body = Shape::new(ShapeType::Rectangle, bx, by, bw, bh)
261        .with_fill(ShapeFill::new("E8EAF6"))
262        .with_line(ShapeLine::new("3F51B5", 12700))
263        .with_text("Body area (60% x 50%)");
264
265    let sidebar_pos = FlexPosition::new(Dimension::percent(68.0), Dimension::percent(35.0));
266    let sidebar_size = FlexSize::new(Dimension::percent(27.0), Dimension::percent(50.0));
267    let (sx, sy) = sidebar_pos.to_emu();
268    let (sw, sh) = sidebar_size.to_emu();
269    let sidebar_shape = Shape::new(ShapeType::Rectangle, sx, sy, sw, sh)
270        .with_fill(ShapeFill::new("FFF3E0"))
271        .with_line(ShapeLine::new("EF6C00", 12700))
272        .with_text("Sidebar (27% x 50%)");
273
274    slides.push(
275        SlideContent::new("FlexPosition & FlexSize — Reusable Layouts")
276            .layout(SlideLayout::TitleOnly)
277            .title_color("1F497D").title_bold(true)
278            .add_shape(header)
279            .add_shape(body)
280            .add_shape(sidebar_shape)
281    );
282
283    // =========================================================================
284    // SLIDE 8: Real-World Dashboard with Dimension API
285    // =========================================================================
286    println!("📊 Slide 8: Real-World Dashboard");
287
288    // 4 evenly-spaced KPI cards using percent
289    let kpi_colors = ["1565C0", "2E7D32", "EF6C00", "7B1FA2"];
290    let kpi_labels = [
291        "Revenue\n$2.14M\n+15%",
292        "Users\n12,450\n+22%",
293        "NPS\n72\n+8 pts",
294        "Uptime\n99.9%\n+0.1%",
295    ];
296
297    let mut dashboard = SlideContent::new("KPI Dashboard — Dimension API")
298        .layout(SlideLayout::TitleOnly)
299        .title_color("1F497D").title_bold(true);
300
301    for i in 0..4 {
302        let x_pct = 3.0 + i as f64 * 24.5;
303        let card = Shape::from_dimensions(ShapeType::RoundedRectangle,
304            Dimension::percent(x_pct), Dimension::percent(22.0),
305            Dimension::percent(22.0), Dimension::percent(30.0),
306        ).with_fill(ShapeFill::new(kpi_colors[i])).with_text(kpi_labels[i]);
307        dashboard = dashboard.add_shape(card);
308    }
309
310    // Bottom chart placeholder
311    let chart_area = Shape::from_dimensions(ShapeType::Rectangle,
312        Dimension::percent(3.0), Dimension::percent(58.0),
313        Dimension::percent(94.0), Dimension::percent(35.0),
314    ).with_fill(ShapeFill::new("ECEFF1"))
315     .with_line(ShapeLine::new("B0BEC5", 12700))
316     .with_text("Chart Area (94% x 35%)");
317    dashboard = dashboard.add_shape(chart_area);
318
319    slides.push(dashboard);
320
321    // =========================================================================
322    // SLIDE 9: Unit Equivalence Reference
323    // =========================================================================
324    println!("📖 Slide 9: Unit Equivalence Reference");
325
326    slides.push(
327        SlideContent::new("Dimension Unit Reference")
328            .layout(SlideLayout::TitleAndContent)
329            .title_color("1F497D").title_bold(true)
330            .add_bullet(&format!("1 inch = {} EMU = Dimension::Inches(1.0)", 914400))
331            .add_bullet(&format!("1 cm   = {} EMU = Dimension::Cm(1.0)", 360000))
332            .add_bullet(&format!("1 pt   = {} EMU = Dimension::Pt(1.0)", 12700))
333            .add_bullet(&format!("Slide width  = {} EMU = 10 inches", SLIDE_WIDTH_EMU))
334            .add_bullet(&format!("Slide height = {} EMU = 7.5 inches", SLIDE_HEIGHT_EMU))
335            .add_bullet("Ratio(0.1) on X = 10% of slide width = 1 inch")
336            .add_bullet("Ratio(0.5) on Y = 50% of slide height = 3.75 inches")
337            .add_bullet("percent(50.0) = Ratio(0.5)")
338            .content_size(22)
339    );
340
341    // =========================================================================
342    // Generate PPTX
343    // =========================================================================
344    fs::create_dir_all("examples/output")?;
345    let num_slides = slides.len();
346    let pptx_data = create_pptx_with_content("Dimension API Demo", slides)?;
347    fs::write("examples/output/dimension_demo.pptx", &pptx_data)?;
348
349    println!("\n╔══════════════════════════════════════════════════════════════╗");
350    println!("║                 Dimension API Demo Complete                   ║");
351    println!("╠══════════════════════════════════════════════════════════════╣");
352    println!("║  Output: examples/output/dimension_demo.pptx                 ║");
353    println!("║  Slides: {}                                                   ║", num_slides);
354    println!("║  Size:   {} KB                                               ║", pptx_data.len() / 1024);
355    println!("╠══════════════════════════════════════════════════════════════╣");
356    println!("║  Showcased:                                                  ║");
357    println!("║    ✓ All 6 unit types: EMU, Inches, Cm, Pt, Ratio, Percent   ║");
358    println!("║    ✓ Shape::from_dimensions() constructor                    ║");
359    println!("║    ✓ Fluent .at() and .with_dimensions() chaining            ║");
360    println!("║    ✓ Mixed-unit positioning                                  ║");
361    println!("║    ✓ Prelude helpers: dim(), rect_ratio(), text_box_ratio()  ║");
362    println!("║    ✓ FlexPosition & FlexSize structs                         ║");
363    println!("║    ✓ Ratio-based grid layout (auto-adapts)                   ║");
364    println!("║    ✓ Real-world KPI dashboard                                ║");
365    println!("╚══════════════════════════════════════════════════════════════╝");
366
367    Ok(())
368}

Trait Implementations§

Source§

impl Clone for ShapeLine

Source§

fn clone(&self) -> ShapeLine

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ShapeLine

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.