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) => {
61 (crate::core::clamp_ratio(*r) * reference_emu as f64) as u32
62 }
63 }
64 }
65
66 pub fn to_emu_x(&self) -> u32 {
68 self.to_emu(SLIDE_WIDTH_EMU)
69 }
70
71 pub fn to_emu_y(&self) -> u32 {
73 self.to_emu(SLIDE_HEIGHT_EMU)
74 }
75}
76
77impl From<u32> for Dimension {
79 fn from(emu: u32) -> Self {
80 Dimension::Emu(emu)
81 }
82}
83
84impl Dimension {
90 pub fn inches(v: f64) -> Self {
92 Dimension::Inches(v)
93 }
94 pub fn cm(v: f64) -> Self {
96 Dimension::Cm(v)
97 }
98 pub fn pt(v: f64) -> Self {
100 Dimension::Pt(v)
101 }
102 pub fn ratio(v: f64) -> Self {
104 Dimension::Ratio(v)
105 }
106 pub fn emu(v: u32) -> Self {
108 Dimension::Emu(v)
109 }
110 pub fn percent(v: f64) -> Self {
112 Dimension::Ratio(v / 100.0)
113 }
114}
115
116#[derive(Clone, Debug)]
118pub struct FlexPosition {
119 pub x: Dimension,
120 pub y: Dimension,
121}
122
123impl FlexPosition {
124 pub fn new(x: Dimension, y: Dimension) -> Self {
125 Self { x, y }
126 }
127
128 pub fn to_emu(&self) -> (u32, u32) {
130 (self.x.to_emu_x(), self.y.to_emu_y())
131 }
132
133 pub fn to_emu_with(&self, slide_width: u32, slide_height: u32) -> (u32, u32) {
135 (self.x.to_emu(slide_width), self.y.to_emu(slide_height))
136 }
137}
138
139#[derive(Clone, Debug)]
141pub struct FlexSize {
142 pub width: Dimension,
143 pub height: Dimension,
144}
145
146impl FlexSize {
147 pub fn new(width: Dimension, height: Dimension) -> Self {
148 Self { width, height }
149 }
150
151 pub fn to_emu(&self) -> (u32, u32) {
153 (self.width.to_emu_x(), self.height.to_emu_y())
154 }
155
156 pub fn to_emu_with(&self, slide_width: u32, slide_height: u32) -> (u32, u32) {
158 (
159 self.width.to_emu(slide_width),
160 self.height.to_emu(slide_height),
161 )
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168
169 #[test]
170 fn test_emu_passthrough() {
171 assert_eq!(Dimension::Emu(914400).to_emu(0), 914400);
172 }
173
174 #[test]
175 fn test_inches_to_emu() {
176 assert_eq!(Dimension::Inches(1.0).to_emu(0), 914400);
177 assert_eq!(Dimension::Inches(0.5).to_emu(0), 457200);
178 assert_eq!(Dimension::Inches(10.0).to_emu(0), 9144000);
179 }
180
181 #[test]
182 fn test_cm_to_emu() {
183 assert_eq!(Dimension::Cm(2.54).to_emu(0), 914400);
184 assert_eq!(Dimension::Cm(1.0).to_emu(0), 360000);
185 }
186
187 #[test]
188 fn test_pt_to_emu() {
189 assert_eq!(Dimension::Pt(72.0).to_emu(0), 914400); assert_eq!(Dimension::Pt(1.0).to_emu(0), 12700);
191 }
192
193 #[test]
194 fn test_ratio_to_emu() {
195 assert_eq!(Dimension::Ratio(0.1).to_emu(SLIDE_WIDTH_EMU), 914400);
197 assert_eq!(Dimension::Ratio(0.5).to_emu(SLIDE_WIDTH_EMU), 4572000);
199 assert_eq!(Dimension::Ratio(1.0).to_emu(SLIDE_WIDTH_EMU), 9144000);
201 assert_eq!(Dimension::Ratio(0.0).to_emu(SLIDE_WIDTH_EMU), 0);
203 }
204
205 #[test]
206 fn test_ratio_clamped() {
207 assert_eq!(Dimension::Ratio(1.5).to_emu(SLIDE_WIDTH_EMU), 9144000);
209 assert_eq!(Dimension::Ratio(-0.5).to_emu(SLIDE_WIDTH_EMU), 0);
211 }
212
213 #[test]
214 fn test_percent() {
215 assert_eq!(Dimension::percent(50.0).to_emu(SLIDE_WIDTH_EMU), 4572000);
216 assert_eq!(Dimension::percent(10.0).to_emu(SLIDE_WIDTH_EMU), 914400);
217 }
218
219 #[test]
220 fn test_to_emu_x_y() {
221 let x = Dimension::Ratio(0.5);
222 let y = Dimension::Ratio(0.5);
223 assert_eq!(x.to_emu_x(), SLIDE_WIDTH_EMU / 2);
224 assert_eq!(y.to_emu_y(), SLIDE_HEIGHT_EMU / 2);
225 }
226
227 #[test]
228 fn test_flex_position() {
229 let pos = FlexPosition::new(Dimension::Inches(1.0), Dimension::Ratio(0.5));
230 let (x, y) = pos.to_emu();
231 assert_eq!(x, 914400);
232 assert_eq!(y, SLIDE_HEIGHT_EMU / 2);
233 }
234
235 #[test]
236 fn test_flex_size() {
237 let size = FlexSize::new(Dimension::Ratio(0.8), Dimension::Inches(2.0));
238 let (w, h) = size.to_emu();
239 assert_eq!(w, (SLIDE_WIDTH_EMU as f64 * 0.8) as u32);
240 assert_eq!(h, 914400 * 2);
241 }
242
243 #[test]
244 fn test_flex_position_custom_slide() {
245 let custom_w = 12192000_u32; let custom_h = 6858000_u32;
247 let pos = FlexPosition::new(Dimension::Ratio(0.5), Dimension::Ratio(0.5));
248 let (x, y) = pos.to_emu_with(custom_w, custom_h);
249 assert_eq!(x, custom_w / 2);
250 assert_eq!(y, custom_h / 2);
251 }
252
253 #[test]
254 fn test_from_u32() {
255 let d: Dimension = 914400_u32.into();
256 assert_eq!(d, Dimension::Emu(914400));
257 }
258
259 #[test]
260 fn test_shorthand_constructors() {
261 assert_eq!(Dimension::inches(1.0), Dimension::Inches(1.0));
262 assert_eq!(Dimension::cm(2.54), Dimension::Cm(2.54));
263 assert_eq!(Dimension::pt(72.0), Dimension::Pt(72.0));
264 assert_eq!(Dimension::ratio(0.5), Dimension::Ratio(0.5));
265 assert_eq!(Dimension::emu(914400), Dimension::Emu(914400));
266 }
267}