1#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
6pub struct Twips(pub i32);
7
8impl Twips {
9 pub fn from_inches(inches: f64) -> Self {
10 Twips((inches * 1440.0) as i32)
11 }
12
13 pub fn from_cm(cm: f64) -> Self {
14 Twips((cm * 567.0) as i32)
15 }
16
17 pub fn from_pt(pt: f64) -> Self {
18 Twips((pt * 20.0) as i32)
19 }
20
21 pub fn to_inches(self) -> f64 {
22 self.0 as f64 / 1440.0
23 }
24
25 pub fn to_cm(self) -> f64 {
26 self.0 as f64 / 567.0
27 }
28
29 pub fn to_pt(self) -> f64 {
30 self.0 as f64 / 20.0
31 }
32
33 pub fn to_emu(self) -> Emu {
34 Emu(self.0 as i64 * 635)
35 }
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
41pub struct Emu(pub i64);
42
43impl Emu {
44 pub fn from_inches(inches: f64) -> Self {
45 Emu((inches * 914400.0) as i64)
46 }
47
48 pub fn from_cm(cm: f64) -> Self {
49 Emu((cm * 360000.0) as i64)
50 }
51
52 pub fn from_pt(pt: f64) -> Self {
53 Emu((pt * 12700.0) as i64)
54 }
55
56 pub fn to_inches(self) -> f64 {
57 self.0 as f64 / 914400.0
58 }
59
60 pub fn to_cm(self) -> f64 {
61 self.0 as f64 / 360000.0
62 }
63
64 pub fn to_pt(self) -> f64 {
65 self.0 as f64 / 12700.0
66 }
67
68 pub fn to_twips(self) -> Twips {
69 Twips((self.0 / 635) as i32)
70 }
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
76pub struct HalfPoint(pub u32);
77
78impl HalfPoint {
79 pub fn from_pt(pt: f64) -> Self {
80 HalfPoint((pt * 2.0) as u32)
81 }
82
83 pub fn to_pt(self) -> f64 {
84 self.0 as f64 / 2.0
85 }
86}
87
88#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
91pub struct Centipoints(pub i32);
92
93impl Centipoints {
94 pub fn from_pt(pt: f64) -> Self {
95 Centipoints((pt * 100.0) as i32)
96 }
97
98 pub fn to_pt(self) -> f64 {
99 self.0 as f64 / 100.0
100 }
101}
102
103#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
106pub struct Angle(pub i32);
107
108impl Angle {
109 pub fn from_degrees(degrees: f64) -> Self {
110 Angle((degrees * 60_000.0) as i32)
111 }
112
113 pub fn to_degrees(self) -> f64 {
114 self.0 as f64 / 60_000.0
115 }
116}
117
118#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
121pub struct Percent1000(pub i32);
122
123impl Percent1000 {
124 pub fn from_percent(percent: f64) -> Self {
125 Percent1000((percent * 1000.0) as i32)
126 }
127
128 pub fn to_fraction(self) -> f64 {
129 self.0 as f64 / 100_000.0
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136
137 #[test]
138 fn twips_conversions() {
139 let t = Twips::from_inches(1.0);
140 assert_eq!(t.0, 1440);
141 assert!((t.to_inches() - 1.0).abs() < 0.001);
142 assert!((t.to_pt() - 72.0).abs() < 0.001);
143 }
144
145 #[test]
146 fn emu_conversions() {
147 let e = Emu::from_inches(1.0);
148 assert_eq!(e.0, 914400);
149 assert!((e.to_inches() - 1.0).abs() < 0.001);
150 }
151
152 #[test]
153 fn half_point_conversion() {
154 let hp = HalfPoint::from_pt(12.0);
155 assert_eq!(hp.0, 24);
156 assert!((hp.to_pt() - 12.0).abs() < 0.001);
157 }
158
159 #[test]
160 fn twips_to_emu_round_trip() {
161 let t = Twips(1440);
162 let e = t.to_emu();
163 let t2 = e.to_twips();
164 assert_eq!(t, t2);
165 }
166
167 #[test]
168 fn twips_float_constructors_truncate_toward_zero() {
169 assert_eq!(Twips::from_inches(1.75 / 1440.0).0, 1);
170 assert_eq!(Twips::from_inches(-1.75 / 1440.0).0, -1);
171 assert_eq!(Twips::from_cm(1.75 / 567.0).0, 1);
172 assert_eq!(Twips::from_cm(-1.75 / 567.0).0, -1);
173 assert_eq!(Twips::from_pt(1.75 / 20.0).0, 1);
174 assert_eq!(Twips::from_pt(-1.75 / 20.0).0, -1);
175 }
176
177 #[test]
178 fn emu_float_constructors_truncate_toward_zero() {
179 assert_eq!(Emu::from_inches(1.75 / 914400.0).0, 1);
180 assert_eq!(Emu::from_inches(-1.75 / 914400.0).0, -1);
181 assert_eq!(Emu::from_cm(1.75 / 360000.0).0, 1);
182 assert_eq!(Emu::from_cm(-1.75 / 360000.0).0, -1);
183 assert_eq!(Emu::from_pt(1.75 / 12700.0).0, 1);
184 assert_eq!(Emu::from_pt(-1.75 / 12700.0).0, -1);
185 }
186
187 #[test]
188 fn centipoints_round_trip_points() {
189 let value = Centipoints::from_pt(18.0);
190 assert_eq!(value.0, 1800);
191 assert_eq!(value.to_pt(), 18.0);
192 }
193
194 #[test]
195 fn angle_round_trip_degrees() {
196 let value = Angle::from_degrees(90.0);
197 assert_eq!(value.0, 5_400_000);
198 assert_eq!(value.to_degrees(), 90.0);
199 }
200
201 #[test]
202 fn percent1000_round_trip_percent() {
203 let value = Percent1000::from_percent(75.0);
204 assert_eq!(value.0, 75_000);
205 assert_eq!(value.to_fraction(), 0.75);
206 }
207
208 #[test]
209 fn new_unit_float_constructors_truncate_toward_zero() {
210 assert_eq!(Centipoints::from_pt(1.75 / 100.0).0, 1);
211 assert_eq!(Centipoints::from_pt(-1.75 / 100.0).0, -1);
212 assert_eq!(Angle::from_degrees(1.75 / 60_000.0).0, 1);
213 assert_eq!(Angle::from_degrees(-1.75 / 60_000.0).0, -1);
214 assert_eq!(Percent1000::from_percent(1.75 / 1000.0).0, 1);
215 assert_eq!(Percent1000::from_percent(-1.75 / 1000.0).0, -1);
216 }
217}