diff options
| author | Laurenz <laurmaedje@gmail.com> | 2023-01-22 13:30:45 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2023-01-22 13:30:45 +0100 |
| commit | 57a636b3704e7002b0f895c0945ec2f720d75584 (patch) | |
| tree | bbed596cebf2d84ff94b17f9b6c32da5214e07c8 | |
| parent | 13cc16b3ccaa94cdf5dca2bf3195067d1d12f9b1 (diff) | |
Math styles
| -rw-r--r-- | library/src/math/style.rs | 402 |
1 files changed, 312 insertions, 90 deletions
diff --git a/library/src/math/style.rs b/library/src/math/style.rs index 0ec6853a..cebb1534 100644 --- a/library/src/math/style.rs +++ b/library/src/math/style.rs @@ -1,11 +1,12 @@ use super::*; -/// # Serif -/// Serif (roman) font style in math. -/// -/// This is already the default. +/// # Bold +/// Bold font style in math. /// -/// _Note:_ In the future this might be unified with text styling. +/// ## Example +/// ``` +/// $ bold(A) := B^+ $ +/// ``` /// /// ## Parameters /// - body: Content (positional, required) @@ -14,35 +15,30 @@ use super::*; /// ## Category /// math #[func] -#[capable(Texify)] +#[capable(LayoutMath)] #[derive(Debug, Hash)] -pub struct SerifNode(pub Content); +pub struct BoldNode(pub Content); #[node] -impl SerifNode { +impl BoldNode { fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> { Ok(Self(args.expect("body")?).pack()) } } -impl Texify for SerifNode { - fn texify(&self, t: &mut Texifier) -> SourceResult<()> { - t.push_str("\\mathrm{"); - self.0.texify_unparen(t)?; - t.push_str("}"); +impl LayoutMath for BoldNode { + fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { + ctx.style(ctx.style.with_italic(false).with_bold_toggled()); + self.0.layout_math(ctx)?; + ctx.unstyle(); Ok(()) } } -/// # Sans-serif -/// Sans-serif font style in math. -/// -/// _Note:_ In the future this might be unified with text styling. +/// # Italic +/// Italic font style in math. /// -/// ## Example -/// ``` -/// $ sans(A B C) $ -/// ``` +/// This is already the default. /// /// ## Parameters /// - body: Content (positional, required) @@ -51,35 +47,30 @@ impl Texify for SerifNode { /// ## Category /// math #[func] -#[capable(Texify)] +#[capable(LayoutMath)] #[derive(Debug, Hash)] -pub struct SansNode(pub Content); +pub struct ItalicNode(pub Content); #[node] -impl SansNode { +impl ItalicNode { fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> { Ok(Self(args.expect("body")?).pack()) } } -impl Texify for SansNode { - fn texify(&self, t: &mut Texifier) -> SourceResult<()> { - t.push_str("\\mathsf{"); - self.0.texify_unparen(t)?; - t.push_str("}"); +impl LayoutMath for ItalicNode { + fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { + ctx.style(ctx.style.with_italic_toggled()); + self.0.layout_math(ctx)?; + ctx.unstyle(); Ok(()) } } -/// # Bold -/// Bold font style in math. -/// -/// _Note:_ In the future this might be unified with text styling. +/// # Serif +/// Serif (roman) font style in math. /// -/// ## Example -/// ``` -/// $ bold(A) := B^+ $ -/// ``` +/// This is already the default. /// /// ## Parameters /// - body: Content (positional, required) @@ -88,32 +79,33 @@ impl Texify for SansNode { /// ## Category /// math #[func] -#[capable(Texify)] +#[capable(LayoutMath)] #[derive(Debug, Hash)] -pub struct BoldNode(pub Content); +pub struct SerifNode(pub Content); #[node] -impl BoldNode { +impl SerifNode { fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> { Ok(Self(args.expect("body")?).pack()) } } -impl Texify for BoldNode { - fn texify(&self, t: &mut Texifier) -> SourceResult<()> { - t.push_str("\\mathbf{"); - self.0.texify_unparen(t)?; - t.push_str("}"); +impl LayoutMath for SerifNode { + fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { + ctx.style(ctx.style.with_variant(MathVariant::Serif)); + self.0.layout_math(ctx)?; + ctx.unstyle(); Ok(()) } } -/// # Italic -/// Italic font style in math. -/// -/// This is already the default. +/// # Sans-serif +/// Sans-serif font style in math. /// -/// _Note:_ In the future this might be unified with text styling. +/// ## Example +/// ``` +/// $ sans(A B C) $ +/// ``` /// /// ## Parameters /// - body: Content (positional, required) @@ -122,22 +114,22 @@ impl Texify for BoldNode { /// ## Category /// math #[func] -#[capable(Texify)] +#[capable(LayoutMath)] #[derive(Debug, Hash)] -pub struct ItalNode(pub Content); +pub struct SansNode(pub Content); #[node] -impl ItalNode { +impl SansNode { fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> { Ok(Self(args.expect("body")?).pack()) } } -impl Texify for ItalNode { - fn texify(&self, t: &mut Texifier) -> SourceResult<()> { - t.push_str("\\mathit{"); - self.0.texify_unparen(t)?; - t.push_str("}"); +impl LayoutMath for SansNode { + fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { + ctx.style(ctx.style.with_variant(MathVariant::Sans)); + self.0.layout_math(ctx)?; + ctx.unstyle(); Ok(()) } } @@ -145,8 +137,6 @@ impl Texify for ItalNode { /// # Calligraphic /// Calligraphic font style in math. /// -/// _Note:_ In the future this might be unified with text styling. -/// /// ## Example /// ``` /// Let $cal(P)$ be the set of ... @@ -159,7 +149,7 @@ impl Texify for ItalNode { /// ## Category /// math #[func] -#[capable(Texify)] +#[capable(LayoutMath)] #[derive(Debug, Hash)] pub struct CalNode(pub Content); @@ -170,11 +160,11 @@ impl CalNode { } } -impl Texify for CalNode { - fn texify(&self, t: &mut Texifier) -> SourceResult<()> { - t.push_str("\\mathcal{"); - self.0.texify_unparen(t)?; - t.push_str("}"); +impl LayoutMath for CalNode { + fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { + ctx.style(ctx.style.with_variant(MathVariant::Cal)); + self.0.layout_math(ctx)?; + ctx.unstyle(); Ok(()) } } @@ -182,8 +172,6 @@ impl Texify for CalNode { /// # Fraktur /// Fraktur font style in math. /// -/// _Note:_ In the future this might be unified with text styling. -/// /// ## Example /// ``` /// $ frak(P) $ @@ -196,7 +184,7 @@ impl Texify for CalNode { /// ## Category /// math #[func] -#[capable(Texify)] +#[capable(LayoutMath)] #[derive(Debug, Hash)] pub struct FrakNode(pub Content); @@ -207,11 +195,11 @@ impl FrakNode { } } -impl Texify for FrakNode { - fn texify(&self, t: &mut Texifier) -> SourceResult<()> { - t.push_str("\\mathfrak{"); - self.0.texify_unparen(t)?; - t.push_str("}"); +impl LayoutMath for FrakNode { + fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { + ctx.style(ctx.style.with_variant(MathVariant::Frak)); + self.0.layout_math(ctx)?; + ctx.unstyle(); Ok(()) } } @@ -219,8 +207,6 @@ impl Texify for FrakNode { /// # Monospace /// Monospace font style in math. /// -/// _Note:_ In the future this might be unified with text styling. -/// /// ## Example /// ``` /// $ mono(x + y = z) $ @@ -233,7 +219,7 @@ impl Texify for FrakNode { /// ## Category /// math #[func] -#[capable(Texify)] +#[capable(LayoutMath)] #[derive(Debug, Hash)] pub struct MonoNode(pub Content); @@ -244,11 +230,11 @@ impl MonoNode { } } -impl Texify for MonoNode { - fn texify(&self, t: &mut Texifier) -> SourceResult<()> { - t.push_str("\\mathtt{"); - self.0.texify_unparen(t)?; - t.push_str("}"); +impl LayoutMath for MonoNode { + fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { + ctx.style(ctx.style.with_variant(MathVariant::Mono)); + self.0.layout_math(ctx)?; + ctx.unstyle(); Ok(()) } } @@ -259,8 +245,6 @@ impl Texify for MonoNode { /// For uppercase latin letters, blackboard bold is additionally available /// through [symmie symbols](@symbol) of the form `NN` and `RR`. /// -/// _Note:_ In the future this might be unified with text styling. -/// /// ## Example /// ``` /// $ bb(b) $ @@ -274,7 +258,7 @@ impl Texify for MonoNode { /// ## Category /// math #[func] -#[capable(Texify)] +#[capable(LayoutMath)] #[derive(Debug, Hash)] pub struct BbNode(pub Content); @@ -285,11 +269,249 @@ impl BbNode { } } -impl Texify for BbNode { - fn texify(&self, t: &mut Texifier) -> SourceResult<()> { - t.push_str("\\mathbb{"); - self.0.texify_unparen(t)?; - t.push_str("}"); +impl LayoutMath for BbNode { + fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { + ctx.style(ctx.style.with_variant(MathVariant::Bb)); + self.0.layout_math(ctx)?; + ctx.unstyle(); Ok(()) } } + +/// The style in a formula. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct MathStyle { + /// The style variant to select. + pub variant: MathVariant, + /// The size of the glyphs. + pub size: MathSize, + /// Affects the height of exponents. + pub cramped: bool, + /// Whether to use bold glyphs. + pub bold: bool, + /// Wherher to use italic glyphs. + pub italic: bool, +} + +/// The size of elements in a formula. +/// +/// See the TeXbook p. 141. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] +pub enum MathSize { + /// Second-level sub- and superscripts. + ScriptScript, + /// Sub- and superscripts. + Script, + /// Math in text. + Text, + /// Math on its own line. + Display, +} + +impl MathStyle { + /// This style, with the given `variant`. + pub fn with_variant(self, variant: MathVariant) -> Self { + Self { variant, ..self } + } + + /// This style, with the given `size`. + pub fn with_size(self, size: MathSize) -> Self { + Self { size, ..self } + } + + /// This style, with `cramped` set to given value. + pub fn with_cramped(self, cramped: bool) -> Self { + Self { cramped, ..self } + } + + /// This style, with `bold` set to given value. + pub fn with_bold(self, bold: bool) -> Self { + Self { bold, ..self } + } + + /// This style, with `italic` set to given value. + pub fn with_italic(self, italic: bool) -> Self { + Self { italic, ..self } + } + + /// This style, with boldness inverted. + pub fn with_bold_toggled(self) -> Self { + self.with_bold(!self.bold) + } + + /// This style, with italicness inverted. + pub fn with_italic_toggled(self) -> Self { + self.with_italic(!self.italic) + } + + /// The style for subscripts in the current style. + pub fn for_subscript(self) -> Self { + self.for_superscript().with_cramped(true) + } + + /// The style for superscripts in the current style. + pub fn for_superscript(self) -> Self { + self.with_size(match self.size { + MathSize::Display | MathSize::Text => MathSize::Script, + MathSize::Script | MathSize::ScriptScript => MathSize::ScriptScript, + }) + } + + /// The style for numerators in the current style. + pub fn for_numerator(self) -> Self { + self.with_size(match self.size { + MathSize::Display => MathSize::Text, + MathSize::Text => MathSize::Script, + MathSize::Script | MathSize::ScriptScript => MathSize::ScriptScript, + }) + } + + /// The style for denominators in the current style. + pub fn for_denominator(self) -> Self { + self.for_numerator().with_cramped(true) + } + + /// Apply the style to a character. + pub fn styled_char(self, c: char) -> char { + styled_char(self, c) + } +} + +/// A mathematical style variant, as defined by Unicode. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum MathVariant { + Serif, + Sans, + Cal, + Frak, + Mono, + Bb, +} + +impl Default for MathVariant { + fn default() -> Self { + Self::Serif + } +} + +/// Select the correct styled math letter. +/// +/// https://www.w3.org/TR/mathml-core/#new-text-transform-mappings +/// https://en.wikipedia.org/wiki/Mathematical_Alphanumeric_Symbols +pub(super) fn styled_char(style: MathStyle, c: char) -> char { + use MathVariant::*; + + let tuple = (style.variant, style.bold, style.italic); + let base = match c { + 'a'..='z' => 'a', + 'A'..='Z' => 'A', + 'α'..='ω' => 'α', + 'Α'..='Ω' => 'Α', + '0'..='9' => '0', + '-' => return '−', + _ => return c, + }; + + let start = match c { + // Latin upper. + 'A'..='Z' => match tuple { + (Serif, false, false) => 0x0041, + (Serif, true, false) => 0x1D400, + (Serif, false, true) => 0x1D434, + (Serif, true, true) => 0x1D468, + (Sans, false, false) => 0x1D5A0, + (Sans, true, false) => 0x1D5D4, + (Sans, false, true) => 0x1D608, + (Sans, true, true) => 0x1D63C, + (Cal, false, _) => 0x1D49C, + (Cal, true, _) => 0x1D4D0, + (Frak, false, _) => 0x1D504, + (Frak, true, _) => 0x1D56C, + (Mono, _, _) => 0x1D670, + (Bb, _, _) => 0x1D538, + }, + + // Latin lower. + 'a'..='z' => match tuple { + (Serif, false, false) => 0x0061, + (Serif, true, false) => 0x1D41A, + (Serif, false, true) => 0x1D44E, + (Serif, true, true) => 0x1D482, + (Sans, false, false) => 0x1D5BA, + (Sans, true, false) => 0x1D5EE, + (Sans, false, true) => 0x1D622, + (Sans, true, true) => 0x1D656, + (Cal, false, _) => 0x1D4B6, + (Cal, true, _) => 0x1D4EA, + (Frak, false, _) => 0x1D51E, + (Frak, true, _) => 0x1D586, + (Mono, _, _) => 0x1D68A, + (Bb, _, _) => 0x1D552, + }, + + // Greek upper. + 'Α'..='Ω' => match tuple { + (Serif, false, false) => 0x0391, + (Serif, true, false) => 0x1D6A8, + (Serif, false, true) => 0x1D6E2, + (Serif, true, true) => 0x1D71C, + (Sans, _, false) => 0x1D756, + (Sans, _, true) => 0x1D790, + (Cal | Frak | Mono | Bb, _, _) => return c, + }, + + // Greek lower. + 'α'..='ω' => match tuple { + (Serif, false, false) => 0x03B1, + (Serif, true, false) => 0x1D6C2, + (Serif, false, true) => 0x1D6FC, + (Serif, true, true) => 0x1D736, + (Sans, _, false) => 0x1D770, + (Sans, _, true) => 0x1D7AA, + (Cal | Frak | Mono | Bb, _, _) => return c, + }, + + // Numbers. + '0'..='9' => match tuple { + (Serif, false, _) => 0x0030, + (Serif, true, _) => 0x1D7CE, + (Bb, _, _) => 0x1D7D8, + (Sans, false, _) => 0x1D7E2, + (Sans, true, _) => 0x1D7EC, + (Mono, _, _) => 0x1D7F6, + (Cal | Frak, _, _) => return c, + }, + + _ => return c, + }; + + // Map and fix up codepoints that are defined in previous Unicode Blocks. + let code = start + (c as u32 - base as u32); + match code { + 0x1D455 => '\u{210E}', + 0x1D49D => '\u{212C}', + 0x1D4A0 => '\u{2130}', + 0x1D4A1 => '\u{2131}', + 0x1D4A3 => '\u{210B}', + 0x1D4A4 => '\u{2110}', + 0x1D4A7 => '\u{2112}', + 0x1D4A8 => '\u{2133}', + 0x1D4AD => '\u{211B}', + 0x1D4BA => '\u{212F}', + 0x1D4BC => '\u{210A}', + 0x1D4C4 => '\u{2134}', + 0x1D506 => '\u{212D}', + 0x1D50B => '\u{210C}', + 0x1D50C => '\u{2111}', + 0x1D515 => '\u{211C}', + 0x1D51D => '\u{2128}', + 0x1D53A => '\u{2102}', + 0x1D53F => '\u{210D}', + 0x1D545 => '\u{2115}', + 0x1D547 => '\u{2119}', + 0x1D548 => '\u{211A}', + 0x1D549 => '\u{211D}', + 0x1D551 => '\u{2124}', + code => std::char::from_u32(code).unwrap(), + } +} |
