pub struct ShapeLine {
pub color: String,
pub width: u32,
}Expand description
Shape line/border properties
Fields§
§color: String§width: u32Implementations§
Source§impl ShapeLine
impl ShapeLine
Sourcepub fn new(color: &str, width: u32) -> Self
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
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§
Auto Trait Implementations§
impl Freeze for ShapeLine
impl RefUnwindSafe for ShapeLine
impl Send for ShapeLine
impl Sync for ShapeLine
impl Unpin for ShapeLine
impl UnsafeUnpin for ShapeLine
impl UnwindSafe for ShapeLine
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more