Skip to main content

rustyfi_backend/
length.rs

1use newer_type::implement;
2use std::ops::{Add, Div, Mul, Neg, Sub};
3
4/// A physical length in PDF points (1/72 inch), the `length.ml` unit.
5///
6/// `Add`, `Sub`, `Neg` and `Mul<f64>` stay hand-written: `newer-type`'s generic
7/// `ops` markers forward the *inner* type's `Output` verbatim, so
8/// `newer_type_std::ops::Add` on an `f64` newtype yields `Output = f64`, not
9/// `Output = Self`, and the macro has no syntax for pinning an associated type
10/// (`newer-type-macro`'s `Implementor::parse` rejects `AssocType` arguments).
11/// Using it would silently change `Length + Length` to `f64`. `AddAssign` has
12/// no `Output` to lose, so it converts cleanly.
13#[implement(newer_type_std::ops::AddAssign)]
14#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)]
15pub struct Length(pub f64);
16
17impl Length {
18    pub const ZERO: Length = Length(0.0);
19
20    pub const fn pt(v: f64) -> Length {
21        Length(v)
22    }
23
24    /// Interpret a `LENGTHCONST` unit suffix (the v0.0.6 unit set).
25    pub fn from_unit(value: f64, unit: &str) -> Option<Length> {
26        let pt = match unit {
27            "pt" => value,
28            "mm" => value * 72.0 / 25.4,
29            "cm" => value * 72.0 / 2.54,
30            "inch" => value * 72.0,
31            _ => return None,
32        };
33        Some(Length(pt))
34    }
35
36    pub fn max(self, other: Length) -> Length {
37        Length(self.0.max(other.0))
38    }
39
40    pub fn min(self, other: Length) -> Length {
41        Length(self.0.min(other.0))
42    }
43
44    pub(crate) fn is_positive(self) -> bool {
45        self.0 > 0.0
46    }
47}
48
49impl Add for Length {
50    type Output = Length;
51    fn add(self, rhs: Length) -> Length {
52        Length(self.0 + rhs.0)
53    }
54}
55
56impl Sub for Length {
57    type Output = Length;
58    fn sub(self, rhs: Length) -> Length {
59        Length(self.0 - rhs.0)
60    }
61}
62
63impl Neg for Length {
64    type Output = Length;
65    fn neg(self) -> Length {
66        Length(-self.0)
67    }
68}
69
70impl Mul<f64> for Length {
71    type Output = Length;
72    fn mul(self, rhs: f64) -> Length {
73        Length(self.0 * rhs)
74    }
75}
76
77impl Div<Length> for Length {
78    type Output = f64;
79    fn div(self, rhs: Length) -> f64 {
80        self.0 / rhs.0
81    }
82}
83
84impl std::fmt::Display for Length {
85    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86        write!(f, "{}pt", self.0)
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn arithmetic() {
96        assert_eq!(Length(2.0) + Length(3.0), Length(5.0));
97        assert_eq!(-Length(1.0), Length(-1.0));
98        assert!(Length::pt(3.0) < Length::pt(4.0));
99        assert_eq!(Length(6.0) / Length(3.0), 2.0);
100        assert_eq!(Length(2.0) * 1.5, Length(3.0));
101        let mut acc = Length(1.0);
102        acc += Length(2.5);
103        assert_eq!(acc, Length(3.5));
104    }
105}