typst_library/math/
equation.rs1use std::num::NonZeroUsize;
2
3use typst_utils::NonZeroExt;
4use unicode_math_class::MathClass;
5
6use crate::diag::SourceResult;
7use crate::engine::Engine;
8use crate::foundations::{
9 elem, Content, NativeElement, Packed, Show, ShowSet, Smart, StyleChain, Styles,
10 Synthesize,
11};
12use crate::introspection::{Count, Counter, CounterUpdate, Locatable};
13use crate::layout::{
14 AlignElem, Alignment, BlockElem, InlineElem, OuterHAlignment, SpecificAlignment,
15 VAlignment,
16};
17use crate::math::{MathSize, MathVariant};
18use crate::model::{Numbering, Outlinable, ParLine, Refable, Supplement};
19use crate::text::{FontFamily, FontList, FontWeight, LocalName, TextElem};
20
21#[elem(Locatable, Synthesize, Show, ShowSet, Count, LocalName, Refable, Outlinable)]
50pub struct EquationElem {
51 #[default(false)]
53 pub block: bool,
54
55 #[borrowed]
67 pub numbering: Option<Numbering>,
68
69 #[default(SpecificAlignment::Both(OuterHAlignment::End, VAlignment::Horizon))]
84 pub number_align: SpecificAlignment<OuterHAlignment, VAlignment>,
85
86 pub supplement: Smart<Option<Supplement>>,
103
104 #[required]
106 pub body: Content,
107
108 #[internal]
110 #[default(MathSize::Text)]
111 #[ghost]
112 pub size: MathSize,
113
114 #[internal]
116 #[ghost]
117 pub variant: MathVariant,
118
119 #[internal]
121 #[default(false)]
122 #[ghost]
123 pub cramped: bool,
124
125 #[internal]
127 #[default(false)]
128 #[ghost]
129 pub bold: bool,
130
131 #[internal]
133 #[ghost]
134 pub italic: Smart<bool>,
135
136 #[internal]
138 #[ghost]
139 pub class: Option<MathClass>,
140
141 #[internal]
144 #[default((70, 50))]
145 #[ghost]
146 pub script_scale: (i16, i16),
147}
148
149impl Synthesize for Packed<EquationElem> {
150 fn synthesize(
151 &mut self,
152 engine: &mut Engine,
153 styles: StyleChain,
154 ) -> SourceResult<()> {
155 let supplement = match self.as_ref().supplement(styles) {
156 Smart::Auto => TextElem::packed(Self::local_name_in(styles)),
157 Smart::Custom(None) => Content::empty(),
158 Smart::Custom(Some(supplement)) => {
159 supplement.resolve(engine, styles, [self.clone().pack()])?
160 }
161 };
162
163 self.push_supplement(Smart::Custom(Some(Supplement::Content(supplement))));
164 Ok(())
165 }
166}
167
168impl Show for Packed<EquationElem> {
169 fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
170 if self.block(styles) {
171 Ok(BlockElem::multi_layouter(
172 self.clone(),
173 engine.routines.layout_equation_block,
174 )
175 .pack()
176 .spanned(self.span()))
177 } else {
178 Ok(InlineElem::layouter(self.clone(), engine.routines.layout_equation_inline)
179 .pack()
180 .spanned(self.span()))
181 }
182 }
183}
184
185impl ShowSet for Packed<EquationElem> {
186 fn show_set(&self, styles: StyleChain) -> Styles {
187 let mut out = Styles::new();
188 if self.block(styles) {
189 out.set(AlignElem::set_alignment(Alignment::CENTER));
190 out.set(BlockElem::set_breakable(false));
191 out.set(ParLine::set_numbering(None));
192 out.set(EquationElem::set_size(MathSize::Display));
193 } else {
194 out.set(EquationElem::set_size(MathSize::Text));
195 }
196 out.set(TextElem::set_weight(FontWeight::from_number(450)));
197 out.set(TextElem::set_font(FontList(vec![FontFamily::new(
198 "New Computer Modern Math",
199 )])));
200 out
201 }
202}
203
204impl Count for Packed<EquationElem> {
205 fn update(&self) -> Option<CounterUpdate> {
206 (self.block(StyleChain::default()) && self.numbering().is_some())
207 .then(|| CounterUpdate::Step(NonZeroUsize::ONE))
208 }
209}
210
211impl LocalName for Packed<EquationElem> {
212 const KEY: &'static str = "equation";
213}
214
215impl Refable for Packed<EquationElem> {
216 fn supplement(&self) -> Content {
217 match (**self).supplement(StyleChain::default()) {
219 Smart::Custom(Some(Supplement::Content(content))) => content,
220 _ => Content::empty(),
221 }
222 }
223
224 fn counter(&self) -> Counter {
225 Counter::of(EquationElem::elem())
226 }
227
228 fn numbering(&self) -> Option<&Numbering> {
229 (**self).numbering(StyleChain::default()).as_ref()
230 }
231}
232
233impl Outlinable for Packed<EquationElem> {
234 fn outlined(&self) -> bool {
235 self.block(StyleChain::default()) && self.numbering().is_some()
236 }
237
238 fn prefix(&self, numbers: Content) -> Content {
239 let supplement = self.supplement();
240 if !supplement.is_empty() {
241 supplement + TextElem::packed('\u{a0}') + numbers
242 } else {
243 numbers
244 }
245 }
246
247 fn body(&self) -> Content {
248 Content::empty()
249 }
250}