From b37f57bdecaed03c569f35a14cde61e880eeea72 Mon Sep 17 00:00:00 2001 From: mkorje Date: Tue, 24 Jun 2025 21:13:47 +1000 Subject: [PATCH] Move math styling to codex --- Cargo.lock | 3 +- Cargo.toml | 2 +- crates/typst-layout/Cargo.toml | 1 + crates/typst-layout/src/math/text.rs | 217 ++-------------------- crates/typst-library/src/math/equation.rs | 7 +- crates/typst-library/src/math/style.rs | 32 ++-- tests/ref/math-style-fallback.png | Bin 0 -> 935 bytes tests/suite/math/style.typ | 11 +- 8 files changed, 44 insertions(+), 229 deletions(-) create mode 100644 tests/ref/math-style-fallback.png diff --git a/Cargo.lock b/Cargo.lock index 550c4141a..87ce0d3f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -413,7 +413,7 @@ dependencies = [ [[package]] name = "codex" version = "0.1.1" -source = "git+https://github.com/typst/codex?rev=a5428cb#a5428cb9c81a41354d44b44dbd5a16a710bbd928" +source = "git+https://github.com/mkorje/codex?rev=f02fa4a#f02fa4a04f2a9937f043892ee865dd06628264ba" [[package]] name = "color-print" @@ -3028,6 +3028,7 @@ version = "0.13.1" dependencies = [ "az", "bumpalo", + "codex", "comemo", "ecow", "hypher", diff --git a/Cargo.toml b/Cargo.toml index 6cc59ee89..c2a58421d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,7 @@ clap = { version = "4.4", features = ["derive", "env", "wrap_help"] } clap_complete = "4.2.1" clap_mangen = "0.2.10" codespan-reporting = "0.11" -codex = { git = "https://github.com/typst/codex", rev = "a5428cb" } +codex = { git = "https://github.com/mkorje/codex", rev = "f02fa4a" } color-print = "0.3.6" comemo = "0.4" csv = "1" diff --git a/crates/typst-layout/Cargo.toml b/crates/typst-layout/Cargo.toml index cc355a3db..2c314e5c5 100644 --- a/crates/typst-layout/Cargo.toml +++ b/crates/typst-layout/Cargo.toml @@ -21,6 +21,7 @@ typst-timing = { workspace = true } typst-utils = { workspace = true } az = { workspace = true } bumpalo = { workspace = true } +codex = { workspace = true } comemo = { workspace = true } ecow = { workspace = true } hypher = { workspace = true } diff --git a/crates/typst-layout/src/math/text.rs b/crates/typst-layout/src/math/text.rs index 67dc0a2c8..d13251e61 100644 --- a/crates/typst-layout/src/math/text.rs +++ b/crates/typst-layout/src/math/text.rs @@ -1,10 +1,11 @@ use std::f64::consts::SQRT_2; +use codex::styling::{to_style, MathStyle}; use ecow::EcoString; use typst_library::diag::SourceResult; use typst_library::foundations::{Packed, StyleChain, SymbolElem}; use typst_library::layout::{Abs, Size}; -use typst_library::math::{EquationElem, MathSize, MathVariant}; +use typst_library::math::{EquationElem, MathSize}; use typst_library::text::{ BottomEdge, BottomEdgeMetric, TextElem, TopEdge, TopEdgeMetric, }; @@ -161,127 +162,22 @@ fn adjust_glyph_layout( } } -/// Style the character by selecting the unicode codepoint for italic, bold, +/// Style the character by selecting the Unicode codepoint for italic, bold, /// caligraphic, etc. -/// -/// -/// fn styled_char(styles: StyleChain, c: char, auto_italic: bool) -> char { - use MathVariant::*; - - let variant = EquationElem::variant_in(styles); - let bold = EquationElem::bold_in(styles); - let italic = EquationElem::italic_in(styles).unwrap_or( - auto_italic - && matches!( - c, - 'a'..='z' | 'ħ' | 'ı' | 'ȷ' | 'A'..='Z' | - 'α'..='ω' | '∂' | 'ϵ' | 'ϑ' | 'ϰ' | 'ϕ' | 'ϱ' | 'ϖ' - ) - && matches!(variant, Sans | Serif), - ); - if let Some(c) = basic_exception(c) { return c; } - if let Some(c) = latin_exception(c, variant, bold, italic) { - return c; - } + let variant = EquationElem::variant_in(styles); + let bold = EquationElem::bold_in(styles); + let italic = + EquationElem::italic_in(styles).or_else(|| (!auto_italic).then_some(false)); + let style = MathStyle::select(c, variant, bold, italic); - if let Some(c) = greek_exception(c, variant, bold, italic) { - return c; - } - - let base = match c { - 'A'..='Z' => 'A', - 'a'..='z' => 'a', - 'Α'..='Ω' => 'Α', - 'α'..='ω' => 'α', - '0'..='9' => '0', - // Hebrew Alef -> Dalet. - '\u{05D0}'..='\u{05D3}' => '\u{05D0}', - _ => return c, - }; - - let tuple = (variant, bold, italic); - 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, - }, - - // Hebrew Alef -> Dalet. - '\u{05D0}'..='\u{05D3}' => 0x2135, - - // 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, - }, - - _ => unreachable!(), - }; - - std::char::from_u32(start + (c as u32 - base as u32)).unwrap() + // At the moment we are only using styles that output a single character, + // so we just grab the first character in the ToStyle iterator. + to_style(c, style).next().unwrap() } fn basic_exception(c: char) -> Option { @@ -290,93 +186,10 @@ fn basic_exception(c: char) -> Option { '〉' => '⟩', '《' => '⟪', '》' => '⟫', - _ => return None, - }) -} - -fn latin_exception( - c: char, - variant: MathVariant, - bold: bool, - italic: bool, -) -> Option { - use MathVariant::*; - Some(match (c, variant, bold, italic) { - ('B', Cal, false, _) => 'ℬ', - ('E', Cal, false, _) => 'ℰ', - ('F', Cal, false, _) => 'ℱ', - ('H', Cal, false, _) => 'ℋ', - ('I', Cal, false, _) => 'ℐ', - ('L', Cal, false, _) => 'ℒ', - ('M', Cal, false, _) => 'ℳ', - ('R', Cal, false, _) => 'ℛ', - ('C', Frak, false, _) => 'ℭ', - ('H', Frak, false, _) => 'ℌ', - ('I', Frak, false, _) => 'ℑ', - ('R', Frak, false, _) => 'ℜ', - ('Z', Frak, false, _) => 'ℨ', - ('C', Bb, ..) => 'ℂ', - ('H', Bb, ..) => 'ℍ', - ('N', Bb, ..) => 'ℕ', - ('P', Bb, ..) => 'ℙ', - ('Q', Bb, ..) => 'ℚ', - ('R', Bb, ..) => 'ℝ', - ('Z', Bb, ..) => 'ℤ', - ('D', Bb, _, true) => 'ⅅ', - ('d', Bb, _, true) => 'ⅆ', - ('e', Bb, _, true) => 'ⅇ', - ('i', Bb, _, true) => 'ⅈ', - ('j', Bb, _, true) => 'ⅉ', - ('h', Serif, false, true) => 'ℎ', - ('e', Cal, false, _) => 'ℯ', - ('g', Cal, false, _) => 'ℊ', - ('o', Cal, false, _) => 'ℴ', - ('ħ', Serif, .., true) => 'ℏ', - ('ı', Serif, .., true) => '𝚤', - ('ȷ', Serif, .., true) => '𝚥', - _ => return None, - }) -} - -fn greek_exception( - c: char, - variant: MathVariant, - bold: bool, - italic: bool, -) -> Option { - use MathVariant::*; - if c == 'Ϝ' && variant == Serif && bold { - return Some('𝟊'); - } - if c == 'ϝ' && variant == Serif && bold { - return Some('𝟋'); - } - - let list = match c { - 'ϴ' => ['𝚹', '𝛳', '𝜭', '𝝧', '𝞡', 'ϴ'], - '∇' => ['𝛁', '𝛻', '𝜵', '𝝯', '𝞩', '∇'], - '∂' => ['𝛛', '𝜕', '𝝏', '𝞉', '𝟃', '∂'], - 'ϵ' => ['𝛜', '𝜖', '𝝐', '𝞊', '𝟄', 'ϵ'], - 'ϑ' => ['𝛝', '𝜗', '𝝑', '𝞋', '𝟅', 'ϑ'], - 'ϰ' => ['𝛞', '𝜘', '𝝒', '𝞌', '𝟆', 'ϰ'], - 'ϕ' => ['𝛟', '𝜙', '𝝓', '𝞍', '𝟇', 'ϕ'], - 'ϱ' => ['𝛠', '𝜚', '𝝔', '𝞎', '𝟈', 'ϱ'], - 'ϖ' => ['𝛡', '𝜛', '𝝕', '𝞏', '𝟉', 'ϖ'], - 'Γ' => ['𝚪', '𝛤', '𝜞', '𝝘', '𝞒', 'ℾ'], - 'γ' => ['𝛄', '𝛾', '𝜸', '𝝲', '𝞬', 'ℽ'], - 'Π' => ['𝚷', '𝛱', '𝜫', '𝝥', '𝞟', 'ℿ'], - 'π' => ['𝛑', '𝜋', '𝝅', '𝝿', '𝞹', 'ℼ'], - '∑' => ['∑', '∑', '∑', '∑', '∑', '⅀'], - _ => return None, - }; - - Some(match (variant, bold, italic) { - (Serif, true, false) => list[0], - (Serif, false, true) => list[1], - (Serif, true, true) => list[2], - (Sans, _, false) => list[3], - (Sans, _, true) => list[4], - (Bb, ..) => list[5], + 'א' => 'ℵ', + 'ב' => 'ℶ', + 'ג' => 'ℷ', + 'ד' => 'ℸ', _ => return None, }) } diff --git a/crates/typst-library/src/math/equation.rs b/crates/typst-library/src/math/equation.rs index 32be216a4..13685d468 100644 --- a/crates/typst-library/src/math/equation.rs +++ b/crates/typst-library/src/math/equation.rs @@ -1,5 +1,6 @@ use std::num::NonZeroUsize; +use codex::styling::MathVariant; use typst_utils::NonZeroExt; use unicode_math_class::MathClass; @@ -14,7 +15,7 @@ use crate::layout::{ AlignElem, Alignment, BlockElem, InlineElem, OuterHAlignment, SpecificAlignment, VAlignment, }; -use crate::math::{MathSize, MathVariant}; +use crate::math::MathSize; use crate::model::{Numbering, Outlinable, ParLine, Refable, Supplement}; use crate::text::{FontFamily, FontList, FontWeight, LocalName, TextElem}; @@ -114,7 +115,7 @@ pub struct EquationElem { /// The style variant to select. #[internal] #[ghost] - pub variant: MathVariant, + pub variant: Option, /// Affects the height of exponents. #[internal] @@ -131,7 +132,7 @@ pub struct EquationElem { /// Whether to use italic glyphs. #[internal] #[ghost] - pub italic: Smart, + pub italic: Option, /// A forced class to use for all fragment. #[internal] diff --git a/crates/typst-library/src/math/style.rs b/crates/typst-library/src/math/style.rs index f3d28f2a9..9d54d7de3 100644 --- a/crates/typst-library/src/math/style.rs +++ b/crates/typst-library/src/math/style.rs @@ -1,4 +1,6 @@ -use crate::foundations::{func, Cast, Content, Smart}; +use codex::styling::MathVariant; + +use crate::foundations::{func, Cast, Content}; use crate::math::EquationElem; /// Bold font style in math. @@ -24,7 +26,7 @@ pub fn upright( /// The content to style. body: Content, ) -> Content { - body.styled(EquationElem::set_italic(Smart::Custom(false))) + body.styled(EquationElem::set_italic(Some(false))) } /// Italic font style in math. @@ -35,7 +37,7 @@ pub fn italic( /// The content to style. body: Content, ) -> Content { - body.styled(EquationElem::set_italic(Smart::Custom(true))) + body.styled(EquationElem::set_italic(Some(true))) } /// Serif (roman) font style in math. @@ -46,7 +48,7 @@ pub fn serif( /// The content to style. body: Content, ) -> Content { - body.styled(EquationElem::set_variant(MathVariant::Serif)) + body.styled(EquationElem::set_variant(Some(MathVariant::Plain))) } /// Sans-serif font style in math. @@ -59,7 +61,7 @@ pub fn sans( /// The content to style. body: Content, ) -> Content { - body.styled(EquationElem::set_variant(MathVariant::Sans)) + body.styled(EquationElem::set_variant(Some(MathVariant::SansSerif))) } /// Calligraphic font style in math. @@ -93,7 +95,7 @@ pub fn cal( /// The content to style. body: Content, ) -> Content { - body.styled(EquationElem::set_variant(MathVariant::Cal)) + body.styled(EquationElem::set_variant(Some(MathVariant::Script))) } /// Fraktur font style in math. @@ -106,7 +108,7 @@ pub fn frak( /// The content to style. body: Content, ) -> Content { - body.styled(EquationElem::set_variant(MathVariant::Frak)) + body.styled(EquationElem::set_variant(Some(MathVariant::Fraktur))) } /// Monospace font style in math. @@ -119,7 +121,7 @@ pub fn mono( /// The content to style. body: Content, ) -> Content { - body.styled(EquationElem::set_variant(MathVariant::Mono)) + body.styled(EquationElem::set_variant(Some(MathVariant::Monospace))) } /// Blackboard bold (double-struck) font style in math. @@ -137,7 +139,7 @@ pub fn bb( /// The content to style. body: Content, ) -> Content { - body.styled(EquationElem::set_variant(MathVariant::Bb)) + body.styled(EquationElem::set_variant(Some(MathVariant::DoubleStruck))) } /// Forced display style in math. @@ -240,15 +242,3 @@ pub enum MathSize { /// Math on its own line. Display, } - -/// A mathematical style variant, as defined by Unicode. -#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Cast, Hash)] -pub enum MathVariant { - #[default] - Serif, - Sans, - Cal, - Frak, - Mono, - Bb, -} diff --git a/tests/ref/math-style-fallback.png b/tests/ref/math-style-fallback.png new file mode 100644 index 0000000000000000000000000000000000000000..de0283762d8208eb46d0d34b6d1727c5692ef64f GIT binary patch literal 935 zcmV;Y16cftP)Nmq2JB?V>G;e89BdSM5) zTJ8q_!hk6J!-Ei;d)EHN(R(1CcTyC_UDxa>Ti1)N-CnI~%S)Z2Fr*;6l=_bqd;`Og z_67rc=Z`cuq@-c*hjrV#0n>A-^8l~-YqtF_4Hungv#@8ak9-Hx@H$WN5cV&&p4_%o z9$rsYRtd<%E6Hj^9){%oWYzRG@^IWuR_#aBaw%BBR|2Q@y}Z110PIbD^up85F;RH= zihr%DJ36p+Vae;0@pta*7lgmP#Q-)IVC&Oc6Tr&Vf^cKrmk!4hHS>TOEtt*#To3ph zj@6})3BvVxeJ-><*Raf?~Ags^r0-i4bW@UzE4QQ<4Y#>qUrRJa9W8(`q~p@RcA~Z?mF1r zde8)za5LLak}7 zVgCw35n$AKuiG&G2CBm(X=Q4F~7Ok6oxmy;dI{hAJ7**{8l(T0)Q_RT6#