Skip to main content

typst_library/math/
mod.rs

1//! Mathematical formulas.
2
3pub mod accent;
4mod attach;
5mod cancel;
6mod equation;
7mod frac;
8pub mod ir;
9mod lr;
10mod matrix;
11mod op;
12mod root;
13mod style;
14mod underover;
15
16pub use self::accent::{ACCENT_SHORT_FALL, Accent, AccentElem};
17pub use self::attach::*;
18pub use self::cancel::*;
19pub use self::equation::*;
20pub use self::frac::*;
21pub use self::lr::*;
22pub use self::matrix::*;
23pub use self::op::*;
24pub use self::root::*;
25pub use self::style::*;
26pub use self::underover::*;
27
28use typst_utils::singleton;
29use unicode_math_class::MathClass;
30
31use crate::foundations::{Content, Module, NativeElement, Scope, StyleChain, elem};
32use crate::layout::{Em, HElem};
33use crate::text::{FontFamily, TextElem};
34
35// Spacings.
36pub const THIN: Em = Em::new(1.0 / 6.0);
37pub const MEDIUM: Em = Em::new(2.0 / 9.0);
38pub const THICK: Em = Em::new(5.0 / 18.0);
39pub const QUAD: Em = Em::new(1.0);
40pub const WIDE: Em = Em::new(2.0);
41
42/// Create a module with all math definitions.
43pub fn module() -> Module {
44    let mut math = Scope::deduplicating();
45    math.start_category(crate::Category::Math);
46    math.define_elem::<EquationElem>();
47    math.define_elem::<TextElem>();
48    math.define_elem::<LrElem>();
49    math.define_elem::<MidElem>();
50    math.define_elem::<AttachElem>();
51    math.define_elem::<StretchElem>();
52    math.define_elem::<ScriptsElem>();
53    math.define_elem::<LimitsElem>();
54    math.define_elem::<AccentElem>();
55    math.define_elem::<UnderlineElem>();
56    math.define_elem::<OverlineElem>();
57    math.define_elem::<UnderbraceElem>();
58    math.define_elem::<OverbraceElem>();
59    math.define_elem::<UnderbracketElem>();
60    math.define_elem::<OverbracketElem>();
61    math.define_elem::<UnderparenElem>();
62    math.define_elem::<OverparenElem>();
63    math.define_elem::<UndershellElem>();
64    math.define_elem::<OvershellElem>();
65    math.define_elem::<CancelElem>();
66    math.define_elem::<FracElem>();
67    math.define_elem::<BinomElem>();
68    math.define_elem::<VecElem>();
69    math.define_elem::<MatElem>();
70    math.define_elem::<CasesElem>();
71    math.define_elem::<RootElem>();
72    math.define_elem::<ClassElem>();
73    math.define_elem::<OpElem>();
74    math.define_elem::<PrimesElem>();
75    math.define_func::<abs>();
76    math.define_func::<norm>();
77    math.define_func::<round>();
78    math.define_func::<sqrt>();
79    math.define_func::<upright>();
80    math.define_func::<bold>();
81    math.define_func::<italic>();
82    math.define_func::<serif>();
83    math.define_func::<sans>();
84    math.define_func::<scr>();
85    math.define_func::<cal>();
86    math.define_func::<frak>();
87    math.define_func::<mono>();
88    math.define_func::<bb>();
89    math.define_func::<display>();
90    math.define_func::<inline>();
91    math.define_func::<script>();
92    math.define_func::<sscript>();
93
94    // Text operators.
95    op::define(&mut math);
96
97    // Spacings.
98    math.define("thin", HElem::new(THIN.into()).pack());
99    math.define("med", HElem::new(MEDIUM.into()).pack());
100    math.define("thick", HElem::new(THICK.into()).pack());
101    math.define("quad", HElem::new(QUAD.into()).pack());
102    math.define("wide", HElem::new(WIDE.into()).pack());
103
104    // Symbols.
105    crate::symbols::define_math(&mut math);
106
107    Module::new("math", math)
108}
109
110/// Trait for recognizing math elements and auto-wrapping them in equations.
111pub trait Mathy {}
112
113/// A math alignment point: `&`, `&&`.
114#[elem(title = "Alignment Point", Mathy)]
115pub struct AlignPointElem {}
116
117impl AlignPointElem {
118    /// Get the globally shared alignment point element.
119    pub fn shared() -> &'static Content {
120        singleton!(Content, AlignPointElem::new().pack())
121    }
122}
123
124/// Forced use of a certain math class.
125///
126/// This is useful to treat certain symbols as if they were of a different
127/// class, e.g. to make a symbol behave like a relation. The class of a symbol
128/// defines the way it is laid out, including spacing around it, and how its
129/// scripts are attached by default. Note that the latter can always be
130/// overridden using @math.limits[`{limits}`] and @math.scripts[`{scripts}`].
131///
132/// = Example <example>
133/// ```example
134/// #let loves = math.class(
135///   "relation",
136///   sym.suit.heart,
137/// )
138///
139/// $x loves y and y loves 5$
140/// ```
141#[elem(Mathy)]
142pub struct ClassElem {
143    /// The class to apply to the content.
144    #[required]
145    pub class: MathClass,
146
147    /// The content to which the class is applied.
148    #[required]
149    pub body: Content,
150}
151
152/// An iterator that alternates between the `Left` and `Right` values, if the
153/// initial value is not `None`.
154#[derive(Debug, Copy, Clone, Eq, PartialEq)]
155pub enum LeftRightAlternator {
156    None,
157    Left,
158    Right,
159}
160
161impl Iterator for LeftRightAlternator {
162    type Item = LeftRightAlternator;
163
164    fn next(&mut self) -> Option<Self::Item> {
165        let r = Some(*self);
166        match self {
167            Self::None => {}
168            Self::Left => *self = Self::Right,
169            Self::Right => *self = Self::Left,
170        }
171        r
172    }
173}
174
175/// Resolve a prioritized iterator over the font families for math.
176pub fn families(styles: StyleChain<'_>) -> impl Iterator<Item = &'_ FontFamily> + Clone {
177    let fallbacks = singleton!(Vec<FontFamily>, {
178        [
179            "new computer modern math",
180            "libertinus serif",
181            "twitter color emoji",
182            "noto color emoji",
183            "apple color emoji",
184            "segoe ui emoji",
185        ]
186        .into_iter()
187        .map(FontFamily::new)
188        .collect()
189    });
190
191    let tail = if styles.get(TextElem::fallback) { fallbacks.as_slice() } else { &[] };
192    styles.get_ref(TextElem::font).into_iter().chain(tail.iter())
193}