pub struct FlexPosition {
pub x: Dimension,
pub y: Dimension,
}Expand description
A 2D position expressed in flexible dimensions.
Fields§
§x: Dimension§y: DimensionImplementations§
Source§impl FlexPosition
impl FlexPosition
Sourcepub fn new(x: Dimension, y: Dimension) -> Self
pub fn new(x: Dimension, y: Dimension) -> Self
Examples found in repository?
examples/dimension_demo.rs (line 248)
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}Sourcepub fn to_emu(&self) -> (u32, u32)
pub fn to_emu(&self) -> (u32, u32)
Resolve to (x_emu, y_emu) using standard slide dimensions
Examples found in repository?
examples/dimension_demo.rs (line 250)
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 FlexPosition
impl Clone for FlexPosition
Source§fn clone(&self) -> FlexPosition
fn clone(&self) -> FlexPosition
Returns a duplicate of the value. Read more
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
Performs copy-assignment from
source. Read moreAuto Trait Implementations§
impl Freeze for FlexPosition
impl RefUnwindSafe for FlexPosition
impl Send for FlexPosition
impl Sync for FlexPosition
impl Unpin for FlexPosition
impl UnwindSafe for FlexPosition
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