summaryrefslogtreecommitdiff
path: root/src/library/math/mod.rs
blob: 1f5ea8f37a65628b25eea2661817af5b57a87f0c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
//! Mathematical formulas.

mod rex;

use crate::library::layout::BlockSpacing;
use crate::library::prelude::*;
use crate::library::text::FontFamily;
use crate::syntax::Spanned;

/// A mathematical formula.
#[derive(Debug, Hash)]
pub struct MathNode {
    /// The formula.
    pub formula: Spanned<EcoString>,
    /// Whether the formula is display-level.
    pub display: bool,
}

#[node(showable)]
impl MathNode {
    /// The math font family.
    #[property(referenced)]
    pub const FAMILY: FontFamily = FontFamily::new("NewComputerModernMath");
    /// The spacing above display math.
    #[property(resolve, shorthand(around))]
    pub const ABOVE: Option<BlockSpacing> = Some(Ratio::one().into());
    /// The spacing below display math.
    #[property(resolve, shorthand(around))]
    pub const BELOW: Option<BlockSpacing> = Some(Ratio::one().into());

    fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
        Ok(Content::show(Self {
            formula: args.expect("formula")?,
            display: args.named("display")?.unwrap_or(false),
        }))
    }
}

impl Show for MathNode {
    fn unguard(&self, _: Selector) -> ShowNode {
        Self { formula: self.formula.clone(), ..*self }.pack()
    }

    fn encode(&self, _: StyleChain) -> Dict {
        dict! {
            "formula" => Value::Str(self.formula.v.clone().into()),
            "display" => Value::Bool(self.display)
        }
    }

    fn realize(
        &self,
        _: Tracked<dyn World>,
        styles: StyleChain,
    ) -> SourceResult<Content> {
        let node = self::rex::RexNode {
            tex: self.formula.clone(),
            display: self.display,
            family: styles.get(Self::FAMILY).clone(),
        };

        Ok(if self.display {
            Content::block(node.pack().aligned(Spec::with_x(Some(Align::Center.into()))))
        } else {
            Content::inline(node)
        })
    }

    fn finalize(
        &self,
        _: Tracked<dyn World>,
        styles: StyleChain,
        mut realized: Content,
    ) -> SourceResult<Content> {
        let mut map = StyleMap::new();
        map.set_family(styles.get(Self::FAMILY).clone(), styles);

        if self.display {
            realized = realized.spaced(styles.get(Self::ABOVE), styles.get(Self::BELOW));
        }

        Ok(realized.styled_with_map(map))
    }
}