mirror of
https://github.com/typst/typst
synced 2025-07-11 14:42:53 +08:00
Move math styling to codex and add math.scr
(#6309)
This commit is contained in:
parent
eed3407051
commit
f9b01f595d
5
Cargo.lock
generated
5
Cargo.lock
generated
@ -413,7 +413,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "codex"
|
name = "codex"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
source = "git+https://github.com/typst/codex?rev=a5428cb#a5428cb9c81a41354d44b44dbd5a16a710bbd928"
|
source = "git+https://github.com/typst/codex?rev=9ac86f9#9ac86f96af5b89fce555e6bba8b6d1ac7b44ef00"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "color-print"
|
name = "color-print"
|
||||||
@ -2861,7 +2861,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "typst-assets"
|
name = "typst-assets"
|
||||||
version = "0.13.1"
|
version = "0.13.1"
|
||||||
source = "git+https://github.com/typst/typst-assets?rev=c1089b4#c1089b46c461bdde579c55caa941a3cc7dec3e8a"
|
source = "git+https://github.com/typst/typst-assets?rev=edf0d64#edf0d648376e29738a05a933af9ea99bb81557b1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typst-cli"
|
name = "typst-cli"
|
||||||
@ -3032,6 +3032,7 @@ version = "0.13.1"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"az",
|
"az",
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
|
"codex",
|
||||||
"comemo",
|
"comemo",
|
||||||
"ecow",
|
"ecow",
|
||||||
"hypher",
|
"hypher",
|
||||||
|
@ -32,7 +32,7 @@ typst-svg = { path = "crates/typst-svg", version = "0.13.1" }
|
|||||||
typst-syntax = { path = "crates/typst-syntax", version = "0.13.1" }
|
typst-syntax = { path = "crates/typst-syntax", version = "0.13.1" }
|
||||||
typst-timing = { path = "crates/typst-timing", version = "0.13.1" }
|
typst-timing = { path = "crates/typst-timing", version = "0.13.1" }
|
||||||
typst-utils = { path = "crates/typst-utils", version = "0.13.1" }
|
typst-utils = { path = "crates/typst-utils", version = "0.13.1" }
|
||||||
typst-assets = { git = "https://github.com/typst/typst-assets", rev = "c1089b4" }
|
typst-assets = { git = "https://github.com/typst/typst-assets", rev = "edf0d64" }
|
||||||
typst-dev-assets = { git = "https://github.com/typst/typst-dev-assets", rev = "bfa947f" }
|
typst-dev-assets = { git = "https://github.com/typst/typst-dev-assets", rev = "bfa947f" }
|
||||||
arrayvec = "0.7.4"
|
arrayvec = "0.7.4"
|
||||||
az = "1.2"
|
az = "1.2"
|
||||||
@ -47,7 +47,7 @@ clap = { version = "4.4", features = ["derive", "env", "wrap_help"] }
|
|||||||
clap_complete = "4.2.1"
|
clap_complete = "4.2.1"
|
||||||
clap_mangen = "0.2.10"
|
clap_mangen = "0.2.10"
|
||||||
codespan-reporting = "0.11"
|
codespan-reporting = "0.11"
|
||||||
codex = { git = "https://github.com/typst/codex", rev = "a5428cb" }
|
codex = { git = "https://github.com/typst/codex", rev = "9ac86f9" }
|
||||||
color-print = "0.3.6"
|
color-print = "0.3.6"
|
||||||
comemo = "0.4"
|
comemo = "0.4"
|
||||||
csv = "1"
|
csv = "1"
|
||||||
|
@ -21,6 +21,7 @@ typst-timing = { workspace = true }
|
|||||||
typst-utils = { workspace = true }
|
typst-utils = { workspace = true }
|
||||||
az = { workspace = true }
|
az = { workspace = true }
|
||||||
bumpalo = { workspace = true }
|
bumpalo = { workspace = true }
|
||||||
|
codex = { workspace = true }
|
||||||
comemo = { workspace = true }
|
comemo = { workspace = true }
|
||||||
ecow = { workspace = true }
|
ecow = { workspace = true }
|
||||||
hypher = { workspace = true }
|
hypher = { workspace = true }
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
use std::f64::consts::SQRT_2;
|
use std::f64::consts::SQRT_2;
|
||||||
|
|
||||||
|
use codex::styling::{to_style, MathStyle};
|
||||||
use ecow::EcoString;
|
use ecow::EcoString;
|
||||||
use typst_library::diag::SourceResult;
|
use typst_library::diag::SourceResult;
|
||||||
use typst_library::foundations::{Packed, StyleChain, SymbolElem};
|
use typst_library::foundations::{Packed, StyleChain, SymbolElem};
|
||||||
use typst_library::layout::{Abs, Size};
|
use typst_library::layout::{Abs, Size};
|
||||||
use typst_library::math::{EquationElem, MathSize, MathVariant};
|
use typst_library::math::{EquationElem, MathSize};
|
||||||
use typst_library::text::{
|
use typst_library::text::{
|
||||||
BottomEdge, BottomEdgeMetric, TextElem, TopEdge, TopEdgeMetric,
|
BottomEdge, BottomEdgeMetric, TextElem, TopEdge, TopEdgeMetric,
|
||||||
};
|
};
|
||||||
@ -64,12 +65,21 @@ fn layout_inline_text(
|
|||||||
ctx: &mut MathContext,
|
ctx: &mut MathContext,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> SourceResult<FrameFragment> {
|
) -> SourceResult<FrameFragment> {
|
||||||
|
let variant = styles.get(EquationElem::variant);
|
||||||
|
let bold = styles.get(EquationElem::bold);
|
||||||
|
// Disable auto-italic.
|
||||||
|
let italic = styles.get(EquationElem::italic).or(Some(false));
|
||||||
|
|
||||||
if text.chars().all(|c| c.is_ascii_digit() || c == '.') {
|
if text.chars().all(|c| c.is_ascii_digit() || c == '.') {
|
||||||
// Small optimization for numbers. Note that this lays out slightly
|
// Small optimization for numbers. Note that this lays out slightly
|
||||||
// differently to normal text and is worth re-evaluating in the future.
|
// differently to normal text and is worth re-evaluating in the future.
|
||||||
let mut fragments = vec![];
|
let mut fragments = vec![];
|
||||||
for unstyled_c in text.chars() {
|
for unstyled_c in text.chars() {
|
||||||
let c = styled_char(styles, unstyled_c, false);
|
// This is fine as ascii digits and '.' can never end up as more
|
||||||
|
// than a single char after styling.
|
||||||
|
let style = MathStyle::select(unstyled_c, variant, bold, italic);
|
||||||
|
let c = to_style(unstyled_c, style).next().unwrap();
|
||||||
|
|
||||||
let glyph = GlyphFragment::new_char(ctx.font, styles, c, span)?;
|
let glyph = GlyphFragment::new_char(ctx.font, styles, c, span)?;
|
||||||
fragments.push(glyph.into());
|
fragments.push(glyph.into());
|
||||||
}
|
}
|
||||||
@ -83,8 +93,10 @@ fn layout_inline_text(
|
|||||||
.map(|p| p.wrap());
|
.map(|p| p.wrap());
|
||||||
|
|
||||||
let styles = styles.chain(&local);
|
let styles = styles.chain(&local);
|
||||||
let styled_text: EcoString =
|
let styled_text: EcoString = text
|
||||||
text.chars().map(|c| styled_char(styles, c, false)).collect();
|
.chars()
|
||||||
|
.flat_map(|c| to_style(c, MathStyle::select(c, variant, bold, italic)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
let spaced = styled_text.graphemes(true).nth(1).is_some();
|
let spaced = styled_text.graphemes(true).nth(1).is_some();
|
||||||
let elem = TextElem::packed(styled_text).spanned(span);
|
let elem = TextElem::packed(styled_text).spanned(span);
|
||||||
@ -124,9 +136,16 @@ pub fn layout_symbol(
|
|||||||
Some(c) if has_dtls_feat(ctx.font) => (c, styles.chain(&dtls)),
|
Some(c) if has_dtls_feat(ctx.font) => (c, styles.chain(&dtls)),
|
||||||
_ => (elem.text, styles),
|
_ => (elem.text, styles),
|
||||||
};
|
};
|
||||||
let c = styled_char(styles, unstyled_c, true);
|
|
||||||
|
let variant = styles.get(EquationElem::variant);
|
||||||
|
let bold = styles.get(EquationElem::bold);
|
||||||
|
let italic = styles.get(EquationElem::italic);
|
||||||
|
|
||||||
|
let style = MathStyle::select(unstyled_c, variant, bold, italic);
|
||||||
|
let text: EcoString = to_style(unstyled_c, style).collect();
|
||||||
|
|
||||||
let fragment: MathFragment =
|
let fragment: MathFragment =
|
||||||
match GlyphFragment::new_char(ctx.font, symbol_styles, c, elem.span()) {
|
match GlyphFragment::new(ctx.font, symbol_styles, &text, elem.span()) {
|
||||||
Ok(mut glyph) => {
|
Ok(mut glyph) => {
|
||||||
adjust_glyph_layout(&mut glyph, ctx, styles);
|
adjust_glyph_layout(&mut glyph, ctx, styles);
|
||||||
glyph.into()
|
glyph.into()
|
||||||
@ -134,8 +153,7 @@ pub fn layout_symbol(
|
|||||||
Err(_) => {
|
Err(_) => {
|
||||||
// Not in the math font, fallback to normal inline text layout.
|
// Not in the math font, fallback to normal inline text layout.
|
||||||
// TODO: Should replace this with proper fallback in [`GlyphFragment::new`].
|
// TODO: Should replace this with proper fallback in [`GlyphFragment::new`].
|
||||||
layout_inline_text(c.encode_utf8(&mut [0; 4]), elem.span(), ctx, styles)?
|
layout_inline_text(&text, elem.span(), ctx, styles)?.into()
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ctx.push(fragment);
|
ctx.push(fragment);
|
||||||
@ -161,226 +179,6 @@ fn adjust_glyph_layout(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Style the character by selecting the unicode codepoint for italic, bold,
|
|
||||||
/// caligraphic, etc.
|
|
||||||
///
|
|
||||||
/// <https://www.w3.org/TR/mathml-core/#new-text-transform-mappings>
|
|
||||||
/// <https://en.wikipedia.org/wiki/Mathematical_Alphanumeric_Symbols>
|
|
||||||
fn styled_char(styles: StyleChain, c: char, auto_italic: bool) -> char {
|
|
||||||
use MathVariant::*;
|
|
||||||
|
|
||||||
let variant = styles.get(EquationElem::variant);
|
|
||||||
let bold = styles.get(EquationElem::bold);
|
|
||||||
let italic = styles.get(EquationElem::italic).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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn basic_exception(c: char) -> Option<char> {
|
|
||||||
Some(match c {
|
|
||||||
'〈' => '⟨',
|
|
||||||
'〉' => '⟩',
|
|
||||||
'《' => '⟪',
|
|
||||||
'》' => '⟫',
|
|
||||||
_ => return None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn latin_exception(
|
|
||||||
c: char,
|
|
||||||
variant: MathVariant,
|
|
||||||
bold: bool,
|
|
||||||
italic: bool,
|
|
||||||
) -> Option<char> {
|
|
||||||
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<char> {
|
|
||||||
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,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The non-dotless version of a dotless character that can be used with the
|
/// The non-dotless version of a dotless character that can be used with the
|
||||||
/// `dtls` OpenType feature.
|
/// `dtls` OpenType feature.
|
||||||
pub fn try_dotless(c: char) -> Option<char> {
|
pub fn try_dotless(c: char) -> Option<char> {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use std::num::NonZeroUsize;
|
use std::num::NonZeroUsize;
|
||||||
|
|
||||||
|
use codex::styling::MathVariant;
|
||||||
use typst_utils::NonZeroExt;
|
use typst_utils::NonZeroExt;
|
||||||
use unicode_math_class::MathClass;
|
use unicode_math_class::MathClass;
|
||||||
|
|
||||||
@ -12,7 +13,7 @@ use crate::introspection::{Count, Counter, CounterUpdate, Locatable};
|
|||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
AlignElem, Alignment, BlockElem, OuterHAlignment, SpecificAlignment, VAlignment,
|
AlignElem, Alignment, BlockElem, OuterHAlignment, SpecificAlignment, VAlignment,
|
||||||
};
|
};
|
||||||
use crate::math::{MathSize, MathVariant};
|
use crate::math::MathSize;
|
||||||
use crate::model::{Numbering, Outlinable, ParLine, Refable, Supplement};
|
use crate::model::{Numbering, Outlinable, ParLine, Refable, Supplement};
|
||||||
use crate::text::{FontFamily, FontList, FontWeight, LocalName, TextElem};
|
use crate::text::{FontFamily, FontList, FontWeight, LocalName, TextElem};
|
||||||
|
|
||||||
@ -111,7 +112,7 @@ pub struct EquationElem {
|
|||||||
/// The style variant to select.
|
/// The style variant to select.
|
||||||
#[internal]
|
#[internal]
|
||||||
#[ghost]
|
#[ghost]
|
||||||
pub variant: MathVariant,
|
pub variant: Option<MathVariant>,
|
||||||
|
|
||||||
/// Affects the height of exponents.
|
/// Affects the height of exponents.
|
||||||
#[internal]
|
#[internal]
|
||||||
@ -128,7 +129,7 @@ pub struct EquationElem {
|
|||||||
/// Whether to use italic glyphs.
|
/// Whether to use italic glyphs.
|
||||||
#[internal]
|
#[internal]
|
||||||
#[ghost]
|
#[ghost]
|
||||||
pub italic: Smart<bool>,
|
pub italic: Option<bool>,
|
||||||
|
|
||||||
/// A forced class to use for all fragment.
|
/// A forced class to use for all fragment.
|
||||||
#[internal]
|
#[internal]
|
||||||
|
@ -80,6 +80,7 @@ pub fn module() -> Module {
|
|||||||
math.define_func::<italic>();
|
math.define_func::<italic>();
|
||||||
math.define_func::<serif>();
|
math.define_func::<serif>();
|
||||||
math.define_func::<sans>();
|
math.define_func::<sans>();
|
||||||
|
math.define_func::<scr>();
|
||||||
math.define_func::<cal>();
|
math.define_func::<cal>();
|
||||||
math.define_func::<frak>();
|
math.define_func::<frak>();
|
||||||
math.define_func::<mono>();
|
math.define_func::<mono>();
|
||||||
|
@ -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;
|
use crate::math::EquationElem;
|
||||||
|
|
||||||
/// Bold font style in math.
|
/// Bold font style in math.
|
||||||
@ -24,7 +26,7 @@ pub fn upright(
|
|||||||
/// The content to style.
|
/// The content to style.
|
||||||
body: Content,
|
body: Content,
|
||||||
) -> Content {
|
) -> Content {
|
||||||
body.set(EquationElem::italic, Smart::Custom(false))
|
body.set(EquationElem::italic, Some(false))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Italic font style in math.
|
/// Italic font style in math.
|
||||||
@ -35,7 +37,7 @@ pub fn italic(
|
|||||||
/// The content to style.
|
/// The content to style.
|
||||||
body: Content,
|
body: Content,
|
||||||
) -> Content {
|
) -> Content {
|
||||||
body.set(EquationElem::italic, Smart::Custom(true))
|
body.set(EquationElem::italic, Some(true))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serif (roman) font style in math.
|
/// Serif (roman) font style in math.
|
||||||
@ -46,7 +48,7 @@ pub fn serif(
|
|||||||
/// The content to style.
|
/// The content to style.
|
||||||
body: Content,
|
body: Content,
|
||||||
) -> Content {
|
) -> Content {
|
||||||
body.set(EquationElem::variant, MathVariant::Serif)
|
body.set(EquationElem::variant, Some(MathVariant::Plain))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sans-serif font style in math.
|
/// Sans-serif font style in math.
|
||||||
@ -59,23 +61,39 @@ pub fn sans(
|
|||||||
/// The content to style.
|
/// The content to style.
|
||||||
body: Content,
|
body: Content,
|
||||||
) -> Content {
|
) -> Content {
|
||||||
body.set(EquationElem::variant, MathVariant::Sans)
|
body.set(EquationElem::variant, Some(MathVariant::SansSerif))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calligraphic font style in math.
|
/// Calligraphic (chancery) font style in math.
|
||||||
///
|
///
|
||||||
/// ```example
|
/// ```example
|
||||||
/// Let $cal(P)$ be the set of ...
|
/// Let $cal(P)$ be the set of ...
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// This corresponds both to LaTeX's `\mathcal` and `\mathscr` as both of these
|
/// This is the default calligraphic/script style for most math fonts. See
|
||||||
/// styles share the same Unicode codepoints. Switching between the styles is
|
/// [`scr`]($math.scr) for more on how to get the other style (roundhand).
|
||||||
/// thus only possible if supported by the font via
|
#[func(title = "Calligraphic", keywords = ["mathcal", "chancery"])]
|
||||||
/// [font features]($text.features).
|
pub fn cal(
|
||||||
|
/// The content to style.
|
||||||
|
body: Content,
|
||||||
|
) -> Content {
|
||||||
|
body.set(EquationElem::variant, Some(MathVariant::Chancery))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Script (roundhand) font style in math.
|
||||||
///
|
///
|
||||||
/// For the default math font, the roundhand style is available through the
|
/// ```example
|
||||||
/// `ss01` feature. Therefore, you could define your own version of `\mathscr`
|
/// $ scr(S) $
|
||||||
/// like this:
|
/// ```
|
||||||
|
///
|
||||||
|
/// There are two ways that fonts can support differentiating `cal` and `scr`.
|
||||||
|
/// The first is using Unicode variation sequences. This works out of the box
|
||||||
|
/// in Typst, however only a few math fonts currently support this.
|
||||||
|
///
|
||||||
|
/// The other way is using [font features]($text.features). For example, the
|
||||||
|
/// roundhand style might be available in a font through the `ss01` feature.
|
||||||
|
/// To use it in Typst, you could then define your own version of `scr` like
|
||||||
|
/// this:
|
||||||
///
|
///
|
||||||
/// ```example
|
/// ```example
|
||||||
/// #let scr(it) = text(
|
/// #let scr(it) = text(
|
||||||
@ -88,12 +106,12 @@ pub fn sans(
|
|||||||
///
|
///
|
||||||
/// (The box is not conceptually necessary, but unfortunately currently needed
|
/// (The box is not conceptually necessary, but unfortunately currently needed
|
||||||
/// due to limitations in Typst's text style handling in math.)
|
/// due to limitations in Typst's text style handling in math.)
|
||||||
#[func(title = "Calligraphic", keywords = ["mathcal", "mathscr"])]
|
#[func(title = "Script Style", keywords = ["mathscr", "roundhand"])]
|
||||||
pub fn cal(
|
pub fn scr(
|
||||||
/// The content to style.
|
/// The content to style.
|
||||||
body: Content,
|
body: Content,
|
||||||
) -> Content {
|
) -> Content {
|
||||||
body.set(EquationElem::variant, MathVariant::Cal)
|
body.set(EquationElem::variant, Some(MathVariant::Roundhand))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fraktur font style in math.
|
/// Fraktur font style in math.
|
||||||
@ -106,7 +124,7 @@ pub fn frak(
|
|||||||
/// The content to style.
|
/// The content to style.
|
||||||
body: Content,
|
body: Content,
|
||||||
) -> Content {
|
) -> Content {
|
||||||
body.set(EquationElem::variant, MathVariant::Frak)
|
body.set(EquationElem::variant, Some(MathVariant::Fraktur))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Monospace font style in math.
|
/// Monospace font style in math.
|
||||||
@ -119,7 +137,7 @@ pub fn mono(
|
|||||||
/// The content to style.
|
/// The content to style.
|
||||||
body: Content,
|
body: Content,
|
||||||
) -> Content {
|
) -> Content {
|
||||||
body.set(EquationElem::variant, MathVariant::Mono)
|
body.set(EquationElem::variant, Some(MathVariant::Monospace))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Blackboard bold (double-struck) font style in math.
|
/// Blackboard bold (double-struck) font style in math.
|
||||||
@ -137,7 +155,7 @@ pub fn bb(
|
|||||||
/// The content to style.
|
/// The content to style.
|
||||||
body: Content,
|
body: Content,
|
||||||
) -> Content {
|
) -> Content {
|
||||||
body.set(EquationElem::variant, MathVariant::Bb)
|
body.set(EquationElem::variant, Some(MathVariant::DoubleStruck))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Forced display style in math.
|
/// Forced display style in math.
|
||||||
@ -240,15 +258,3 @@ pub enum MathSize {
|
|||||||
/// Math on its own line.
|
/// Math on its own line.
|
||||||
Display,
|
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,
|
|
||||||
}
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
title: Variants
|
title: Variants
|
||||||
category: math
|
category: math
|
||||||
path: ["math"]
|
path: ["math"]
|
||||||
filter: ["serif", "sans", "frak", "mono", "bb", "cal"]
|
filter: ["serif", "sans", "frak", "mono", "bb", "cal", "scr"]
|
||||||
details: |
|
details: |
|
||||||
Alternate typefaces within formulas.
|
Alternate typefaces within formulas.
|
||||||
|
|
||||||
|
BIN
tests/ref/math-style-fallback.png
Normal file
BIN
tests/ref/math-style-fallback.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 935 B |
Binary file not shown.
Before Width: | Height: | Size: 296 B After Width: | Height: | Size: 489 B |
BIN
tests/ref/math-style-script.png
Normal file
BIN
tests/ref/math-style-script.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 585 B |
@ -12,6 +12,15 @@ $A, italic(A), upright(A), bold(A), bold(upright(A)), \
|
|||||||
bb("hello") + bold(cal("world")), \
|
bb("hello") + bold(cal("world")), \
|
||||||
mono("SQRT")(x) wreath mono(123 + 456)$
|
mono("SQRT")(x) wreath mono(123 + 456)$
|
||||||
|
|
||||||
|
--- math-style-fallback ---
|
||||||
|
// Test how math styles fallback.
|
||||||
|
$upright(frak(bold(alpha))) = upright(bold(alpha)) \
|
||||||
|
bold(mono(ϝ)) = bold(ϝ) \
|
||||||
|
sans(Theta) = bold(sans(Theta)) \
|
||||||
|
bold(upright(planck)) != planck \
|
||||||
|
bb(e) != italic(bb(e)) \
|
||||||
|
serif(sans(A)) != serif(A)$
|
||||||
|
|
||||||
--- math-style-dotless ---
|
--- math-style-dotless ---
|
||||||
// Test styling dotless i and j.
|
// Test styling dotless i and j.
|
||||||
$ dotless.i dotless.j,
|
$ dotless.i dotless.j,
|
||||||
@ -21,7 +30,7 @@ $ dotless.i dotless.j,
|
|||||||
bb(dotless.i) bb(dotless.j),
|
bb(dotless.i) bb(dotless.j),
|
||||||
cal(dotless.i) cal(dotless.j),
|
cal(dotless.i) cal(dotless.j),
|
||||||
frak(dotless.i) frak(dotless.j),
|
frak(dotless.i) frak(dotless.j),
|
||||||
mono(dotless.i) mono(dotless.j),
|
mono(dotless.i) mono(dotless.j),
|
||||||
bold(frak(dotless.i)) upright(sans(dotless.j)),
|
bold(frak(dotless.i)) upright(sans(dotless.j)),
|
||||||
italic(bb(dotless.i)) frak(sans(dotless.j)) $
|
italic(bb(dotless.i)) frak(sans(dotless.j)) $
|
||||||
|
|
||||||
@ -38,7 +47,15 @@ $bb(Gamma) , bb(gamma), bb(Pi), bb(pi), bb(sum)$
|
|||||||
|
|
||||||
--- math-style-hebrew-exceptions ---
|
--- math-style-hebrew-exceptions ---
|
||||||
// Test hebrew exceptions.
|
// Test hebrew exceptions.
|
||||||
$aleph, beth, gimel, daleth$
|
$aleph, beth, gimel, daleth$ \
|
||||||
|
$upright(aleph), upright(beth), upright(gimel), upright(daleth)$
|
||||||
|
|
||||||
|
--- math-style-script ---
|
||||||
|
// Test variation selectors for scr and cal.
|
||||||
|
$cal(A) scr(A) bold(cal(O)) scr(bold(O))$
|
||||||
|
|
||||||
|
#show math.equation: set text(font: "Noto Sans Math")
|
||||||
|
$scr(E) cal(E) bold(scr(Y)) cal(bold(Y))$
|
||||||
|
|
||||||
--- issue-3650-italic-equation ---
|
--- issue-3650-italic-equation ---
|
||||||
_abc $sin(x) "abc"$_ \
|
_abc $sin(x) "abc"$_ \
|
||||||
|
Loading…
x
Reference in New Issue
Block a user