1pub const SLIDE_WIDTH_EMU: u32 = 9144000;
22pub const SLIDE_HEIGHT_EMU: u32 = 6858000;
24
25const EMU_PER_INCH: f64 = 914400.0;
27const EMU_PER_CM: f64 = 360000.0;
29const EMU_PER_PT: f64 = 12700.0;
31
32#[derive(Clone, Debug, PartialEq)]
37pub enum Dimension {
38 Emu(u32),
40 Inches(f64),
42 Cm(f64),
44 Pt(f64),
46 Ratio(f64),
48}
49
50impl Dimension {
51 pub fn to_emu(&self, reference_emu: u32) -> u32 {
55 match self {
56 Dimension::Emu(v) => *v,
57 Dimension::Inches(v) => (v * EMU_PER_INCH) as u32,
58 Dimension::Cm(v) => (v * EMU_PER_CM) as u32,
59 Dimension::Pt(v) => (v * EMU_PER_PT) as u32,
60 Dimension::Ratio(r) => (r.clamp(0.0, 1.0) * reference_emu as f64) as u32,
61 }
62 }
63
64 pub fn to_emu_x(&self) -> u32 {
66 self.to_emu(SLIDE_WIDTH_EMU)
67 }
68
69 pub fn to_emu_y(&self) -> u32 {
71 self.to_emu(SLIDE_HEIGHT_EMU)
72 }
73}
74
75impl From<u32> for Dimension {
77 fn from(emu: u32) -> Self {
78 Dimension::Emu(emu)
79 }
80}
81
82impl Dimension {
88 pub fn inches(v: f64) -> Self {
90 Dimension::Inches(v)
91 }
92 pub fn cm(v: f64) -> Self {
94 Dimension::Cm(v)
95 }
96 pub fn pt(v: f64) -> Self {
98 Dimension::Pt(v)
99 }
100 pub fn ratio(v: f64) -> Self {
102 Dimension::Ratio(v)
103 }
104 pub fn emu(v: u32) -> Self {
106 Dimension::Emu(v)
107 }
108 pub fn percent(v: f64) -> Self {
110 Dimension::Ratio(v / 100.0)
111 }
112}
113
114#[derive(Clone, Debug)]
116pub struct FlexPosition {
117 pub x: Dimension,
118 pub y: Dimension,
119}
120
121impl FlexPosition {
122 pub fn new(x: Dimension, y: Dimension) -> Self {
123 Self { x, y }
124 }
125
126 pub fn to_emu(&self) -> (u32, u32) {
128 (self.x.to_emu_x(), self.y.to_emu_y())
129 }
130
131 pub fn to_emu_with(&self, slide_width: u32, slide_height: u32) -> (u32, u32) {
133 (self.x.to_emu(slide_width), self.y.to_emu(slide_height))
134 }
135}
136
137#[derive(Clone, Debug)]
139pub struct FlexSize {
140 pub width: Dimension,
141 pub height: Dimension,
142}
143
144impl FlexSize {
145 pub fn new(width: Dimension, height: Dimension) -> Self {
146 Self { width, height }
147 }
148
149 pub fn to_emu(&self) -> (u32, u32) {
151 (self.width.to_emu_x(), self.height.to_emu_y())
152 }
153
154 pub fn to_emu_with(&self, slide_width: u32, slide_height: u32) -> (u32, u32) {
156 (
157 self.width.to_emu(slide_width),
158 self.height.to_emu(slide_height),
159 )
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166
167 #[test]
168 fn test_emu_passthrough() {
169 assert_eq!(Dimension::Emu(914400).to_emu(0), 914400);
170 }
171
172 #[test]
173 fn test_inches_to_emu() {
174 assert_eq!(Dimension::Inches(1.0).to_emu(0), 914400);
175 assert_eq!(Dimension::Inches(0.5).to_emu(0), 457200);
176 assert_eq!(Dimension::Inches(10.0).to_emu(0), 9144000);
177 }
178
179 #[test]
180 fn test_cm_to_emu() {
181 assert_eq!(Dimension::Cm(2.54).to_emu(0), 914400);
182 assert_eq!(Dimension::Cm(1.0).to_emu(0), 360000);
183 }
184
185 #[test]
186 fn test_pt_to_emu() {
187 assert_eq!(Dimension::Pt(72.0).to_emu(0), 914400); assert_eq!(Dimension::Pt(1.0).to_emu(0), 12700);
189 }
190
191 #[test]
192 fn test_ratio_to_emu() {
193 assert_eq!(Dimension::Ratio(0.1).to_emu(SLIDE_WIDTH_EMU), 914400);
195 assert_eq!(Dimension::Ratio(0.5).to_emu(SLIDE_WIDTH_EMU), 4572000);
197 assert_eq!(Dimension::Ratio(1.0).to_emu(SLIDE_WIDTH_EMU), 9144000);
199 assert_eq!(Dimension::Ratio(0.0).to_emu(SLIDE_WIDTH_EMU), 0);
201 }
202
203 #[test]
204 fn test_ratio_clamped() {
205 assert_eq!(Dimension::Ratio(1.5).to_emu(SLIDE_WIDTH_EMU), 9144000);
207 assert_eq!(Dimension::Ratio(-0.5).to_emu(SLIDE_WIDTH_EMU), 0);
209 }
210
211 #[test]
212 fn test_percent() {
213 assert_eq!(Dimension::percent(50.0).to_emu(SLIDE_WIDTH_EMU), 4572000);
214 assert_eq!(Dimension::percent(10.0).to_emu(SLIDE_WIDTH_EMU), 914400);
215 }
216
217 #[test]
218 fn test_to_emu_x_y() {
219 let x = Dimension::Ratio(0.5);
220 let y = Dimension::Ratio(0.5);
221 assert_eq!(x.to_emu_x(), SLIDE_WIDTH_EMU / 2);
222 assert_eq!(y.to_emu_y(), SLIDE_HEIGHT_EMU / 2);
223 }
224
225 #[test]
226 fn test_flex_position() {
227 let pos = FlexPosition::new(Dimension::Inches(1.0), Dimension::Ratio(0.5));
228 let (x, y) = pos.to_emu();
229 assert_eq!(x, 914400);
230 assert_eq!(y, SLIDE_HEIGHT_EMU / 2);
231 }
232
233 #[test]
234 fn test_flex_size() {
235 let size = FlexSize::new(Dimension::Ratio(0.8), Dimension::Inches(2.0));
236 let (w, h) = size.to_emu();
237 assert_eq!(w, (SLIDE_WIDTH_EMU as f64 * 0.8) as u32);
238 assert_eq!(h, 914400 * 2);
239 }
240
241 #[test]
242 fn test_flex_position_custom_slide() {
243 let custom_w = 12192000_u32; let custom_h = 6858000_u32;
245 let pos = FlexPosition::new(Dimension::Ratio(0.5), Dimension::Ratio(0.5));
246 let (x, y) = pos.to_emu_with(custom_w, custom_h);
247 assert_eq!(x, custom_w / 2);
248 assert_eq!(y, custom_h / 2);
249 }
250
251 #[test]
252 fn test_from_u32() {
253 let d: Dimension = 914400_u32.into();
254 assert_eq!(d, Dimension::Emu(914400));
255 }
256
257 #[test]
258 fn test_shorthand_constructors() {
259 assert_eq!(Dimension::inches(1.0), Dimension::Inches(1.0));
260 assert_eq!(Dimension::cm(2.54), Dimension::Cm(2.54));
261 assert_eq!(Dimension::pt(72.0), Dimension::Pt(72.0));
262 assert_eq!(Dimension::ratio(0.5), Dimension::Ratio(0.5));
263 assert_eq!(Dimension::emu(914400), Dimension::Emu(914400));
264 }
265}