typst_library/math/
attach.rs

1use crate::foundations::{elem, Content, Packed};
2use crate::layout::{Length, Rel};
3use crate::math::{EquationElem, Mathy};
4
5/// A base with optional attachments.
6///
7/// ```example
8/// $ attach(
9///   Pi, t: alpha, b: beta,
10///   tl: 1, tr: 2+3, bl: 4+5, br: 6,
11/// ) $
12/// ```
13#[elem(Mathy)]
14pub struct AttachElem {
15    /// The base to which things are attached.
16    #[required]
17    pub base: Content,
18
19    /// The top attachment, smartly positioned at top-right or above the base.
20    ///
21    /// You can wrap the base in `{limits()}` or `{scripts()}` to override the
22    /// smart positioning.
23    pub t: Option<Content>,
24
25    /// The bottom attachment, smartly positioned at the bottom-right or below
26    /// the base.
27    ///
28    /// You can wrap the base in `{limits()}` or `{scripts()}` to override the
29    /// smart positioning.
30    pub b: Option<Content>,
31
32    /// The top-left attachment (before the base).
33    pub tl: Option<Content>,
34
35    /// The bottom-left attachment (before base).
36    pub bl: Option<Content>,
37
38    /// The top-right attachment (after the base).
39    pub tr: Option<Content>,
40
41    /// The bottom-right attachment (after the base).
42    pub br: Option<Content>,
43}
44
45impl Packed<AttachElem> {
46    /// If an AttachElem's base is also an AttachElem, merge attachments into the
47    /// base AttachElem where possible.
48    pub fn merge_base(&self) -> Option<Self> {
49        // Extract from an EquationElem.
50        let mut base = &self.base;
51        while let Some(equation) = base.to_packed::<EquationElem>() {
52            base = &equation.body;
53        }
54
55        // Move attachments from elem into base where possible.
56        if let Some(base) = base.to_packed::<AttachElem>() {
57            let mut elem = self.clone();
58            let mut base = base.clone();
59
60            macro_rules! merge {
61                ($content:ident) => {
62                    if base.$content.is_none() && elem.$content.is_some() {
63                        base.$content = elem.$content.clone();
64                        elem.$content = None;
65                    }
66                };
67            }
68
69            merge!(t);
70            merge!(b);
71            merge!(tl);
72            merge!(tr);
73            merge!(bl);
74            merge!(br);
75
76            elem.base = base.pack();
77            return Some(elem);
78        }
79
80        None
81    }
82}
83
84/// Grouped primes.
85///
86/// ```example
87/// $ a'''_b = a^'''_b $
88/// ```
89///
90/// # Syntax
91/// This function has dedicated syntax: use apostrophes instead of primes. They
92/// will automatically attach to the previous element, moving superscripts to
93/// the next level.
94#[elem(Mathy)]
95pub struct PrimesElem {
96    /// The number of grouped primes.
97    #[required]
98    pub count: usize,
99}
100
101/// Forces a base to display attachments as scripts.
102///
103/// ```example
104/// $ scripts(sum)_1^2 != sum_1^2 $
105/// ```
106#[elem(Mathy)]
107pub struct ScriptsElem {
108    /// The base to attach the scripts to.
109    #[required]
110    pub body: Content,
111}
112
113/// Forces a base to display attachments as limits.
114///
115/// ```example
116/// $ limits(A)_1^2 != A_1^2 $
117/// ```
118#[elem(Mathy)]
119pub struct LimitsElem {
120    /// The base to attach the limits to.
121    #[required]
122    pub body: Content,
123
124    /// Whether to also force limits in inline equations.
125    ///
126    /// When applying limits globally (e.g., through a show rule), it is
127    /// typically a good idea to disable this.
128    #[default(true)]
129    pub inline: bool,
130}
131
132/// Stretches a glyph.
133///
134/// This function can also be used to automatically stretch the base of an
135/// attachment, so that it fits the top and bottom attachments.
136///
137/// Note that only some glyphs can be stretched, and which ones can depend on
138/// the math font being used. However, most math fonts are the same in this
139/// regard.
140///
141/// ```example
142/// $ H stretch(=)^"define" U + p V $
143/// $ f : X stretch(->>, size: #150%)_"surjective" Y $
144/// $ x stretch(harpoons.ltrb, size: #3em) y
145///     stretch(\[, size: #150%) z $
146/// ```
147#[elem(Mathy)]
148pub struct StretchElem {
149    /// The glyph to stretch.
150    #[required]
151    pub body: Content,
152
153    /// The size to stretch to, relative to the maximum size of the glyph and
154    /// its attachments.
155    #[resolve]
156    #[default(Rel::one())]
157    pub size: Rel<Length>,
158}