diff --git a/library/src/math/ctx.rs b/library/src/math/ctx.rs index a6487476a..a1dc6cf4d 100644 --- a/library/src/math/ctx.rs +++ b/library/src/math/ctx.rs @@ -31,6 +31,7 @@ pub struct MathContext<'a, 'b, 'v> { pub ttf: &'a ttf_parser::Face<'a>, pub table: ttf_parser::math::Table<'a>, pub constants: ttf_parser::math::Constants<'a>, + pub ssty_table: Option>, pub space_width: Em, pub fragments: Vec, pub local: Styles, @@ -50,6 +51,27 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> { ) -> Self { let table = font.ttf().tables().math.unwrap(); let constants = table.constants.unwrap(); + + let ssty_table = font + .ttf() + .tables() + .gsub + .and_then(|gsub| { + gsub.features + .find(ttf_parser::Tag::from_bytes(b"ssty")) + .and_then(|feature| feature.lookup_indices.get(0)) + .and_then(|index| gsub.lookups.get(index)) + }) + .and_then(|ssty| { + ssty.subtables.get::(0) + }) + .and_then(|ssty| match ssty { + ttf_parser::gsub::SubstitutionSubtable::Alternate(alt_glyphs) => { + Some(alt_glyphs) + } + _ => None, + }); + let size = TextElem::size_in(styles); let ttf = font.ttf(); let space_width = ttf @@ -66,6 +88,7 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> { ttf: font.ttf(), table, constants, + ssty_table, space_width, fragments: vec![], local: Styles::new(), @@ -129,20 +152,31 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> { let text = elem.text(); let span = elem.span(); let mut chars = text.chars(); - let fragment = if let Some(glyph) = chars + let fragment = if let Some(mut glyph) = chars .next() .filter(|_| chars.next().is_none()) .map(|c| self.style.styled_char(c)) .and_then(|c| GlyphFragment::try_new(self, c, span)) { // A single letter that is available in the math font. - if self.style.size == MathSize::Display - && glyph.class == Some(MathClass::Large) - { - let height = scaled!(self, display_operator_min_height); - glyph.stretch_vertical(self, height, Abs::zero()).into() - } else { - glyph.into() + match self.style.size { + MathSize::Display => { + if glyph.class == Some(MathClass::Large) { + let height = scaled!(self, display_operator_min_height); + glyph.stretch_vertical(self, height, Abs::zero()).into() + } else { + glyph.into() + } + } + MathSize::Script => { + glyph.make_scriptsize(self); + glyph.into() + } + MathSize::ScriptScript => { + glyph.make_scriptscriptsize(self); + glyph.into() + } + _ => glyph.into(), } } else if text.chars().all(|c| c.is_ascii_digit()) { // Numbers aren't that difficult. diff --git a/library/src/math/fragment.rs b/library/src/math/fragment.rs index b70a3f7dc..139ce07b1 100644 --- a/library/src/math/fragment.rs +++ b/library/src/math/fragment.rs @@ -1,4 +1,5 @@ use super::*; +use ttf_parser::gsub::AlternateSet; #[derive(Debug, Clone)] pub enum MathFragment { @@ -272,6 +273,25 @@ impl GlyphFragment { frame.meta_iter(self.meta); frame } + + pub fn make_scriptsize(&mut self, ctx: &MathContext) { + let alt_id = + script_alternatives(ctx, self.id).and_then(|alts| alts.alternates.get(0)); + + if let Some(alt_id) = alt_id { + self.set_id(ctx, alt_id); + } + } + + pub fn make_scriptscriptsize(&mut self, ctx: &MathContext) { + let alts = script_alternatives(ctx, self.id); + let alt_id = alts + .and_then(|alts| alts.alternates.get(1).or_else(|| alts.alternates.get(0))); + + if let Some(alt_id) = alt_id { + self.set_id(ctx, alt_id); + } + } } impl Debug for GlyphFragment { @@ -347,6 +367,16 @@ fn italics_correction(ctx: &MathContext, id: GlyphId) -> Option { Some(ctx.table.glyph_info?.italic_corrections?.get(id)?.scaled(ctx)) } +/// Look up the script/scriptscript alternates for a glyph +fn script_alternatives<'a>( + ctx: &MathContext<'a, '_, '_>, + id: GlyphId, +) -> Option> { + ctx.ssty_table.and_then(|ssty| { + ssty.coverage.get(id).and_then(|index| ssty.alternate_sets.get(index)) + }) +} + /// Look up the italics correction for a glyph. fn is_extended_shape(ctx: &MathContext, id: GlyphId) -> bool { ctx.table diff --git a/tests/ref/bugs/math-realize.png b/tests/ref/bugs/math-realize.png index 4f5b887ed..2a17f6297 100644 Binary files a/tests/ref/bugs/math-realize.png and b/tests/ref/bugs/math-realize.png differ diff --git a/tests/ref/compiler/content-field.png b/tests/ref/compiler/content-field.png index d2f696b42..cef3274a5 100644 Binary files a/tests/ref/compiler/content-field.png and b/tests/ref/compiler/content-field.png differ diff --git a/tests/ref/layout/enum-numbering.png b/tests/ref/layout/enum-numbering.png index 28755dcb0..e1b2103bb 100644 Binary files a/tests/ref/layout/enum-numbering.png and b/tests/ref/layout/enum-numbering.png differ diff --git a/tests/ref/math/accent.png b/tests/ref/math/accent.png index 11de9e286..5a963b385 100644 Binary files a/tests/ref/math/accent.png and b/tests/ref/math/accent.png differ diff --git a/tests/ref/math/attach.png b/tests/ref/math/attach.png index 894f553f4..9d01e7bf0 100644 Binary files a/tests/ref/math/attach.png and b/tests/ref/math/attach.png differ diff --git a/tests/ref/math/cancel.png b/tests/ref/math/cancel.png index 96b29c45f..571edcc2b 100644 Binary files a/tests/ref/math/cancel.png and b/tests/ref/math/cancel.png differ diff --git a/tests/ref/math/cases.png b/tests/ref/math/cases.png index c9eca24c2..c824a8012 100644 Binary files a/tests/ref/math/cases.png and b/tests/ref/math/cases.png differ diff --git a/tests/ref/math/content.png b/tests/ref/math/content.png index f7af513b0..ce727e66e 100644 Binary files a/tests/ref/math/content.png and b/tests/ref/math/content.png differ diff --git a/tests/ref/math/delimited.png b/tests/ref/math/delimited.png index e9f6e2c14..31d15b410 100644 Binary files a/tests/ref/math/delimited.png and b/tests/ref/math/delimited.png differ diff --git a/tests/ref/math/frac.png b/tests/ref/math/frac.png index a116c876a..803275781 100644 Binary files a/tests/ref/math/frac.png and b/tests/ref/math/frac.png differ diff --git a/tests/ref/math/matrix.png b/tests/ref/math/matrix.png index d3d2681c7..bf90e7128 100644 Binary files a/tests/ref/math/matrix.png and b/tests/ref/math/matrix.png differ diff --git a/tests/ref/math/multiline.png b/tests/ref/math/multiline.png index ae847434d..84dcb87d5 100644 Binary files a/tests/ref/math/multiline.png and b/tests/ref/math/multiline.png differ diff --git a/tests/ref/math/numbering.png b/tests/ref/math/numbering.png index 6256310f8..3952dea04 100644 Binary files a/tests/ref/math/numbering.png and b/tests/ref/math/numbering.png differ diff --git a/tests/ref/math/op.png b/tests/ref/math/op.png index 08d8b93ba..15c7329dd 100644 Binary files a/tests/ref/math/op.png and b/tests/ref/math/op.png differ diff --git a/tests/ref/math/opticalsize.png b/tests/ref/math/opticalsize.png new file mode 100644 index 000000000..9fec5520b Binary files /dev/null and b/tests/ref/math/opticalsize.png differ diff --git a/tests/ref/math/root.png b/tests/ref/math/root.png index 8a6a6e131..017582a41 100644 Binary files a/tests/ref/math/root.png and b/tests/ref/math/root.png differ diff --git a/tests/ref/math/spacing.png b/tests/ref/math/spacing.png index 2fd30e538..abcfae805 100644 Binary files a/tests/ref/math/spacing.png and b/tests/ref/math/spacing.png differ diff --git a/tests/ref/math/style.png b/tests/ref/math/style.png index 39ecc4417..cec04ba50 100644 Binary files a/tests/ref/math/style.png and b/tests/ref/math/style.png differ diff --git a/tests/ref/math/syntax.png b/tests/ref/math/syntax.png index 3d09723d6..028e21d6b 100644 Binary files a/tests/ref/math/syntax.png and b/tests/ref/math/syntax.png differ diff --git a/tests/ref/math/unbalanced.png b/tests/ref/math/unbalanced.png index 5b4d77363..de0ee1db2 100644 Binary files a/tests/ref/math/unbalanced.png and b/tests/ref/math/unbalanced.png differ diff --git a/tests/ref/math/underover.png b/tests/ref/math/underover.png index 5c825b18b..24c96b21f 100644 Binary files a/tests/ref/math/underover.png and b/tests/ref/math/underover.png differ diff --git a/tests/ref/meta/figure.png b/tests/ref/meta/figure.png index 524f6cfd7..90f59d147 100644 Binary files a/tests/ref/meta/figure.png and b/tests/ref/meta/figure.png differ diff --git a/tests/typ/math/op.typ b/tests/typ/math/op.typ index 08395de0b..2a0f84fc6 100644 --- a/tests/typ/math/op.typ +++ b/tests/typ/math/op.typ @@ -11,6 +11,7 @@ $ &sin x + log_2 x \ --- // Test scripts vs limits. +#set page(width: auto) #set text(font: "New Computer Modern") Discuss $lim_(n->oo) 1/n$ now. $ lim_(n->infinity) 1/n = 0 $ diff --git a/tests/typ/math/opticalsize.typ b/tests/typ/math/opticalsize.typ new file mode 100644 index 000000000..c96e4a325 --- /dev/null +++ b/tests/typ/math/opticalsize.typ @@ -0,0 +1,30 @@ +// test optical sized variants in sub/superscripts + +--- + +// Test transition from script to scriptscript. +#[ +#set text(size:20pt) +$ e^(e^(e^(e))) $ +] +A large number: $e^(e^(e^(e)))$. + +--- +// Test prime/double prime via scriptsize +#let prime = [ \u{2032} ] +#let dprime = [ \u{2033} ] +#let tprime = [ \u{2034} ] +$ y^dprime-2y^prime + y = 0 $ +$y^dprime-2y^prime + y = 0$ +$ y^tprime_3 + g^(prime 2) $ + +--- +// Test prime superscript on large symbol +$ scripts(sum_(k in NN))^prime 1/k^2 $ +$sum_(k in NN)^prime 1/k^2$ + +--- +// Test script-script in a fraction. +$ 1/(x^A) $ +#[#set text(size:18pt); $1/(x^A)$] vs. #[#set text(size:14pt); $x^A$] +