ppt_rs/elements/
position.rs1use crate::core::ToXml;
9
10pub const EMU_PER_INCH: i64 = 914400;
12pub const EMU_PER_CM: i64 = 360000;
13pub const EMU_PER_MM: i64 = 36000;
14pub const EMU_PER_PT: i64 = 12700;
15
16#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
18pub struct Position {
19 pub x: i64,
20 pub y: i64,
21}
22
23impl Position {
24 pub fn new(x: i64, y: i64) -> Self {
26 Self { x, y }
27 }
28
29 pub fn from_inches(x: f64, y: f64) -> Self {
31 Self {
32 x: (x * EMU_PER_INCH as f64) as i64,
33 y: (y * EMU_PER_INCH as f64) as i64,
34 }
35 }
36
37 pub fn from_cm(x: f64, y: f64) -> Self {
39 Self {
40 x: (x * EMU_PER_CM as f64) as i64,
41 y: (y * EMU_PER_CM as f64) as i64,
42 }
43 }
44
45 pub fn x_inches(&self) -> f64 {
47 self.x as f64 / EMU_PER_INCH as f64
48 }
49
50 pub fn y_inches(&self) -> f64 {
52 self.y as f64 / EMU_PER_INCH as f64
53 }
54}
55
56#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
58pub struct Size {
59 pub width: i64,
60 pub height: i64,
61}
62
63impl Size {
64 pub fn new(width: i64, height: i64) -> Self {
66 Self { width, height }
67 }
68
69 pub fn from_inches(width: f64, height: f64) -> Self {
71 Self {
72 width: (width * EMU_PER_INCH as f64) as i64,
73 height: (height * EMU_PER_INCH as f64) as i64,
74 }
75 }
76
77 pub fn from_cm(width: f64, height: f64) -> Self {
79 Self {
80 width: (width * EMU_PER_CM as f64) as i64,
81 height: (height * EMU_PER_CM as f64) as i64,
82 }
83 }
84
85 pub fn width_inches(&self) -> f64 {
87 self.width as f64 / EMU_PER_INCH as f64
88 }
89
90 pub fn height_inches(&self) -> f64 {
92 self.height as f64 / EMU_PER_INCH as f64
93 }
94}
95
96#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
98pub struct Transform {
99 pub position: Position,
100 pub size: Size,
101 pub rotation: i32, }
103
104impl Transform {
105 pub fn new(position: Position, size: Size) -> Self {
107 Self {
108 position,
109 size,
110 rotation: 0,
111 }
112 }
113
114 pub fn from_inches(x: f64, y: f64, width: f64, height: f64) -> Self {
116 Self {
117 position: Position::from_inches(x, y),
118 size: Size::from_inches(width, height),
119 rotation: 0,
120 }
121 }
122
123 pub fn with_rotation(mut self, degrees: f64) -> Self {
125 self.rotation = (degrees * 60000.0) as i32;
126 self
127 }
128}
129
130impl ToXml for Transform {
131 fn to_xml(&self) -> String {
132 let mut xml = String::from("<a:xfrm");
133 if self.rotation != 0 {
134 xml.push_str(&format!(r#" rot="{}""#, self.rotation));
135 }
136 xml.push_str(&format!(
137 r#"><a:off x="{}" y="{}"/><a:ext cx="{}" cy="{}"/></a:xfrm>"#,
138 self.position.x, self.position.y, self.size.width, self.size.height
139 ));
140 xml
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[test]
149 fn test_position_from_inches() {
150 let pos = Position::from_inches(1.0, 2.0);
151 assert_eq!(pos.x, 914400);
152 assert_eq!(pos.y, 1828800);
153 }
154
155 #[test]
156 fn test_size_from_inches() {
157 let size = Size::from_inches(3.0, 2.0);
158 assert_eq!(size.width, 2743200);
159 assert_eq!(size.height, 1828800);
160 }
161
162 #[test]
163 fn test_transform_to_xml() {
164 let transform = Transform::from_inches(1.0, 1.0, 2.0, 1.5);
165 let xml = transform.to_xml();
166 assert!(xml.contains("a:xfrm"));
167 assert!(xml.contains("a:off"));
168 assert!(xml.contains("a:ext"));
169 }
170
171 #[test]
172 fn test_transform_with_rotation() {
173 let transform = Transform::from_inches(0.0, 0.0, 1.0, 1.0)
174 .with_rotation(45.0);
175 let xml = transform.to_xml();
176 assert!(xml.contains("rot=\"2700000\"")); }
178
179 #[test]
180 fn test_emu_constants() {
181 assert_eq!(EMU_PER_INCH, 914400);
182 assert_eq!(EMU_PER_CM, 360000);
183 assert_eq!(EMU_PER_PT, 12700);
184 }
185}