typst_library/math/style.rs
1use codex::styling::MathVariant;
2use smallvec::smallvec;
3use typst_utils::LazyHash;
4
5use crate::foundations::{Cast, Content, Style, StyleChain, func};
6use crate::math::EquationElem;
7use crate::text::{FontFeatures, Tag, TextElem};
8
9/// Bold font style in math.
10///
11/// ```example
12/// $ bold(A) := B^+ $
13/// ```
14#[func(keywords = ["mathbf"])]
15pub fn bold(
16 /// The content to style.
17 body: Content,
18) -> Content {
19 body.set(EquationElem::bold, true)
20}
21
22/// Upright (non-italic) font style in math.
23///
24/// ```example
25/// $ upright(A) != A $
26/// ```
27#[func(keywords = ["mathup"])]
28pub fn upright(
29 /// The content to style.
30 body: Content,
31) -> Content {
32 body.set(EquationElem::italic, Some(false))
33}
34
35/// Italic font style in math.
36///
37/// For roman letters and greek lowercase letters, this is already the default.
38#[func(keywords = ["mathit"])]
39pub fn italic(
40 /// The content to style.
41 body: Content,
42) -> Content {
43 body.set(EquationElem::italic, Some(true))
44}
45
46/// Serif (roman) font style in math.
47///
48/// This is already the default.
49#[func(keywords = ["mathrm"])]
50pub fn serif(
51 /// The content to style.
52 body: Content,
53) -> Content {
54 body.set(EquationElem::variant, Some(MathVariant::Plain))
55}
56
57/// Sans-serif font style in math.
58///
59/// ```example
60/// $ sans(A B C) $
61/// ```
62#[func(title = "Sans Serif", keywords = ["mathsf"])]
63pub fn sans(
64 /// The content to style.
65 body: Content,
66) -> Content {
67 body.set(EquationElem::variant, Some(MathVariant::SansSerif))
68}
69
70/// Calligraphic (chancery) font style in math.
71///
72/// ```example
73/// Let $cal(P)$ be the set of ...
74/// ```
75///
76/// This is the default calligraphic/script style for most math fonts. See
77/// @math.scr[`scr`] for more on how to get the other style (roundhand).
78#[func(title = "Calligraphic", keywords = ["mathcal", "chancery"])]
79pub fn cal(
80 /// The content to style.
81 body: Content,
82) -> Content {
83 body.set(EquationElem::variant, Some(MathVariant::Chancery))
84}
85
86/// Script (roundhand) font style in math.
87///
88/// ```example
89/// $scr(L)$ is not the set of linear
90/// maps $cal(L)$.
91/// ```
92///
93/// There are two ways that fonts can support differentiating `cal` and `scr`.
94/// The first is using Unicode variation sequences. This works out of the box in
95/// Typst, however only a few math fonts currently support this.
96///
97/// The other way is using @text.features[font features]. For example, the
98/// roundhand style might be available in a font through the
99/// _@text.stylistic-set[stylistic set] 1_ (`ss01`) feature. To use it in Typst,
100/// you could then define your own version of `scr` like in the example below.
101///
102/// #example(
103/// title: "Recreation using stylistic set 1",
104/// ```
105/// #let scr(it) = text(
106/// stylistic-set: 1,
107/// $cal(it)$,
108/// )
109///
110/// We establish $cal(P) != scr(P)$.
111/// ```
112/// )
113#[func(title = "Script Style", keywords = ["mathscr", "roundhand"])]
114pub fn scr(
115 /// The content to style.
116 body: Content,
117) -> Content {
118 body.set(EquationElem::variant, Some(MathVariant::Roundhand))
119}
120
121/// Fraktur font style in math.
122///
123/// ```example
124/// $ frak(P) $
125/// ```
126#[func(title = "Fraktur", keywords = ["mathfrak"])]
127pub fn frak(
128 /// The content to style.
129 body: Content,
130) -> Content {
131 body.set(EquationElem::variant, Some(MathVariant::Fraktur))
132}
133
134/// Monospace font style in math.
135///
136/// ```example
137/// $ mono(x + y = z) $
138/// ```
139#[func(title = "Monospace", keywords = ["mathtt"])]
140pub fn mono(
141 /// The content to style.
142 body: Content,
143) -> Content {
144 body.set(EquationElem::variant, Some(MathVariant::Monospace))
145}
146
147/// Blackboard bold (double-struck) font style in math.
148///
149/// For uppercase latin letters, blackboard bold is additionally available
150/// through @sym[symbols] of the form `NN` and `RR`.
151///
152/// ```example
153/// $ bb(b) $
154/// $ bb(N) = NN $
155/// $ f: NN -> RR $
156/// ```
157#[func(title = "Blackboard Bold", keywords = ["mathbb"])]
158pub fn bb(
159 /// The content to style.
160 body: Content,
161) -> Content {
162 body.set(EquationElem::variant, Some(MathVariant::DoubleStruck))
163}
164
165/// Forced display style in math.
166///
167/// This is the normal size for block equations.
168///
169/// ```example
170/// $sum_i x_i/2 = display(sum_i x_i/2)$
171/// ```
172#[func(title = "Display Size", keywords = ["displaystyle"])]
173pub fn display(
174 /// The content to size.
175 body: Content,
176 /// Whether to impose a height restriction for exponents, like regular sub-
177 /// and superscripts do.
178 #[named]
179 #[default(false)]
180 cramped: bool,
181) -> Content {
182 body.set(EquationElem::size, MathSize::Display)
183 .set(EquationElem::cramped, cramped)
184}
185
186/// Forced inline (text) style in math.
187///
188/// This is the normal size for inline equations.
189///
190/// ```example
191/// $ sum_i x_i/2
192/// = inline(sum_i x_i/2) $
193/// ```
194#[func(title = "Inline Size", keywords = ["textstyle"])]
195pub fn inline(
196 /// The content to size.
197 body: Content,
198 /// Whether to impose a height restriction for exponents, like regular sub-
199 /// and superscripts do.
200 #[named]
201 #[default(false)]
202 cramped: bool,
203) -> Content {
204 body.set(EquationElem::size, MathSize::Text)
205 .set(EquationElem::cramped, cramped)
206}
207
208/// Forced script style in math.
209///
210/// This is the smaller size used in powers or sub- or superscripts.
211///
212/// ```example
213/// $sum_i x_i/2 = script(sum_i x_i/2)$
214/// ```
215#[func(title = "Script Size", keywords = ["scriptstyle"])]
216pub fn script(
217 /// The content to size.
218 body: Content,
219 /// Whether to impose a height restriction for exponents, like regular sub-
220 /// and superscripts do.
221 #[named]
222 #[default(true)]
223 cramped: bool,
224) -> Content {
225 body.set(EquationElem::size, MathSize::Script)
226 .set(EquationElem::cramped, cramped)
227}
228
229/// Forced second script style in math.
230///
231/// This is the smallest size, used in second-level sub- and superscripts
232/// (script of the script).
233///
234/// ```example
235/// $sum_i x_i/2 = sscript(sum_i x_i/2)$
236/// ```
237#[func(title = "Script-Script Size", keywords = ["scriptscriptstyle"])]
238pub fn sscript(
239 /// The content to size.
240 body: Content,
241 /// Whether to impose a height restriction for exponents, like regular sub-
242 /// and superscripts do.
243 #[named]
244 #[default(true)]
245 cramped: bool,
246) -> Content {
247 body.set(EquationElem::size, MathSize::ScriptScript)
248 .set(EquationElem::cramped, cramped)
249}
250
251/// The size of elements in an equation.
252///
253/// See the TeXbook p. 141.
254///
255/// In MathML Core the attributes `displaystyle` and `scriptlevel` correspond
256/// to the CSS properties `math-style` and `math-depth`.
257/// - `displaystyle="true"` is equivalent to `math-style: normal`
258/// - `displaystyle="false"` is equivalent to `math-style: compact`
259/// - `scriptlevel="n"` is equivalent to `math-depth: n`
260#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Cast)]
261pub enum MathSize {
262 /// Second-level sub- and superscripts.
263 ///
264 /// This is equivalent (in MathML Core) to `displaystyle` and `scriptlevel`
265 /// as `false` and `2`.
266 ScriptScript,
267 /// Sub- and superscripts.
268 ///
269 /// This is equivalent (in MathML Core) to `displaystyle` and `scriptlevel`
270 /// as `false` and `1`.
271 Script,
272 /// Math in text.
273 ///
274 /// This is equivalent (in MathML Core) to `displaystyle` and `scriptlevel`
275 /// as `false` and `0`.
276 Text,
277 /// Math on its own line.
278 ///
279 /// This is equivalent (in MathML Core) to `displaystyle` and `scriptlevel`
280 /// as `true` and `0`.
281 Display,
282}
283
284/// Sets flac OpenType feature.
285pub fn style_flac() -> LazyHash<Style> {
286 TextElem::features
287 .set(FontFeatures(smallvec![(Tag::from_bytes(b"flac"), 1)]))
288 .wrap()
289}
290
291/// Sets dtls OpenType feature.
292pub fn style_dtls() -> LazyHash<Style> {
293 TextElem::features
294 .set(FontFeatures(smallvec![(Tag::from_bytes(b"dtls"), 1)]))
295 .wrap()
296}
297
298/// Styles something as cramped.
299///
300/// This is equivalent (in MathML Core) to the following CSS:
301/// ```css
302/// math-shift: compact;
303/// ```
304pub fn style_cramped() -> LazyHash<Style> {
305 EquationElem::cramped.set(true).wrap()
306}
307
308/// The style for superscripts in the current style.
309///
310/// This is equivalent (in MathML Core) to the following CSS:
311/// ```css
312/// math-style: compact;
313/// math-depth: add(1);
314/// ```
315pub fn style_for_superscript(styles: StyleChain) -> LazyHash<Style> {
316 EquationElem::size
317 .set(match styles.get(EquationElem::size) {
318 MathSize::Display | MathSize::Text => MathSize::Script,
319 MathSize::Script | MathSize::ScriptScript => MathSize::ScriptScript,
320 })
321 .wrap()
322}
323
324/// The style for subscripts in the current style.
325///
326/// This is equivalent (in MathML Core) to the following CSS:
327/// ```css
328/// math-style: compact;
329/// math-depth: add(1);
330/// math-shift: compact;
331/// ```
332pub fn style_for_subscript(styles: StyleChain) -> [LazyHash<Style>; 2] {
333 [style_for_superscript(styles), style_cramped()]
334}
335
336/// The style for numerators in the current style.
337///
338/// This is equivalent (in MathML Core) to the following CSS:
339/// ```css
340/// math-style: compact;
341/// math-depth: auto-add;
342/// ```
343pub fn style_for_numerator(styles: StyleChain) -> LazyHash<Style> {
344 EquationElem::size
345 .set(match styles.get(EquationElem::size) {
346 MathSize::Display => MathSize::Text,
347 MathSize::Text => MathSize::Script,
348 MathSize::Script | MathSize::ScriptScript => MathSize::ScriptScript,
349 })
350 .wrap()
351}
352
353/// The style for denominators in the current style.
354///
355/// This is equivalent (in MathML Core) to the following CSS:
356/// ```css
357/// math-style: compact;
358/// math-depth: auto-add;
359/// math-shift: compact;
360/// ```
361pub fn style_for_denominator(styles: StyleChain) -> [LazyHash<Style>; 2] {
362 [style_for_numerator(styles), style_cramped()]
363}