Skip to main content

typst_library/layout/
em.rs

1use std::fmt::{self, Debug, Formatter};
2use std::iter::Sum;
3use std::ops::{Add, Div, Mul, Neg};
4
5use ecow::EcoString;
6use typst_utils::{Numeric, NumericLength, Scalar};
7
8use crate::foundations::{Repr, Resolve, StyleChain, Value, cast, repr};
9use crate::layout::{Abs, Length};
10use crate::text::TextElem;
11
12/// A length that is relative to the font size.
13///
14/// `1em` is the same as the font size.
15#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
16pub struct Em(Scalar);
17
18impl Em {
19    /// The zero em length.
20    pub const fn zero() -> Self {
21        Self(Scalar::ZERO)
22    }
23
24    /// The font size.
25    pub const fn one() -> Self {
26        Self(Scalar::ONE)
27    }
28
29    /// Creates a font-relative length.
30    pub const fn new(em: f64) -> Self {
31        Self(Scalar::new(em))
32    }
33
34    /// Creates an em length from font units at the given units per em.
35    pub fn from_units(units: impl Into<f64>, units_per_em: f64) -> Self {
36        Self(Scalar::new(units.into() / units_per_em))
37    }
38
39    /// Creates an em length from an absolute length at the given font size.
40    pub fn from_abs(length: Abs, font_size: Abs) -> Self {
41        let result = length / font_size;
42        if result.is_finite() { Self(Scalar::new(result)) } else { Self::zero() }
43    }
44
45    /// Creates an em length from a length at the given font size.
46    pub fn from_length(length: Length, font_size: Abs) -> Em {
47        length.em + Self::from_abs(length.abs, font_size)
48    }
49
50    /// The number of em units.
51    pub const fn get(self) -> f64 {
52        (self.0).get()
53    }
54
55    /// The absolute value of this em length.
56    pub fn abs(self) -> Self {
57        Self::new(self.get().abs())
58    }
59
60    /// Converts to an absolute length at the given font size.
61    pub fn at(self, font_size: Abs) -> Abs {
62        let resolved = font_size * self.get();
63        if resolved.is_finite() { resolved } else { Abs::zero() }
64    }
65}
66
67impl NumericLength for Em {}
68
69impl Numeric for Em {
70    fn zero() -> Self {
71        Self::zero()
72    }
73
74    fn is_finite(self) -> bool {
75        self.0.is_finite()
76    }
77}
78
79impl Debug for Em {
80    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
81        write!(f, "{:?}em", self.get())
82    }
83}
84
85impl Repr for Em {
86    fn repr(&self) -> EcoString {
87        repr::format_float_with_unit(self.get(), "em")
88    }
89}
90
91impl Neg for Em {
92    type Output = Self;
93
94    fn neg(self) -> Self {
95        Self(-self.0)
96    }
97}
98
99impl Add for Em {
100    type Output = Self;
101
102    fn add(self, other: Self) -> Self {
103        Self(self.0 + other.0)
104    }
105}
106
107typst_utils::sub_impl!(Em - Em -> Em);
108
109impl Mul<f64> for Em {
110    type Output = Self;
111
112    fn mul(self, other: f64) -> Self {
113        Self(self.0 * other)
114    }
115}
116
117impl Mul<Em> for f64 {
118    type Output = Em;
119
120    fn mul(self, other: Em) -> Em {
121        other * self
122    }
123}
124
125impl Div<f64> for Em {
126    type Output = Self;
127
128    fn div(self, other: f64) -> Self {
129        Self(self.0 / other)
130    }
131}
132
133impl Div for Em {
134    type Output = f64;
135
136    fn div(self, other: Self) -> f64 {
137        self.get() / other.get()
138    }
139}
140
141typst_utils::assign_impl!(Em += Em);
142typst_utils::assign_impl!(Em -= Em);
143typst_utils::assign_impl!(Em *= f64);
144typst_utils::assign_impl!(Em /= f64);
145
146impl Sum for Em {
147    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
148        Self(iter.map(|s| s.0).sum())
149    }
150}
151
152cast! {
153     Em,
154     self => Value::Length(self.into()),
155}
156
157impl Resolve for Em {
158    type Output = Abs;
159
160    fn resolve(self, styles: StyleChain) -> Self::Output {
161        if self.is_zero() { Abs::zero() } else { self.at(styles.resolve(TextElem::size)) }
162    }
163}