diff --git a/crates/typst/src/math/fragment.rs b/crates/typst/src/math/fragment.rs index cad79c67b..64bc30784 100644 --- a/crates/typst/src/math/fragment.rs +++ b/crates/typst/src/math/fragment.rs @@ -18,7 +18,7 @@ pub enum MathFragment { Glyph(GlyphFragment), Variant(VariantFragment), Frame(FrameFragment), - Spacing(Abs), + Spacing(SpacingFragment), Space(Abs), Linebreak, Align, @@ -34,7 +34,7 @@ impl MathFragment { Self::Glyph(glyph) => glyph.width, Self::Variant(variant) => variant.frame.width(), Self::Frame(fragment) => fragment.frame.width(), - Self::Spacing(amount) => *amount, + Self::Spacing(spacing) => spacing.width, Self::Space(amount) => *amount, _ => Abs::zero(), } @@ -205,6 +205,12 @@ impl From for MathFragment { } } +impl From for MathFragment { + fn from(fragment: SpacingFragment) -> Self { + Self::Spacing(fragment) + } +} + #[derive(Clone)] pub struct GlyphFragment { pub id: GlyphId, @@ -467,6 +473,12 @@ impl FrameFragment { } } +#[derive(Debug, Clone)] +pub struct SpacingFragment { + pub width: Abs, + pub weak: bool, +} + /// Look up the italics correction for a glyph. fn italics_correction(ctx: &MathContext, id: GlyphId) -> Option { Some(ctx.table.glyph_info?.italic_corrections?.get(id)?.scaled(ctx)) diff --git a/crates/typst/src/math/lr.rs b/crates/typst/src/math/lr.rs index 35012050f..4a1e758b6 100644 --- a/crates/typst/src/math/lr.rs +++ b/crates/typst/src/math/lr.rs @@ -3,7 +3,9 @@ use unicode_math_class::MathClass; use crate::diag::SourceResult; use crate::foundations::{elem, func, Content, NativeElement, Resolve, Smart}; use crate::layout::{Abs, Em, Length, Rel}; -use crate::math::{GlyphFragment, LayoutMath, MathContext, MathFragment, Scaled}; +use crate::math::{ + GlyphFragment, LayoutMath, MathContext, MathFragment, Scaled, SpacingFragment, +}; use crate::text::TextElem; /// How much less high scaled delimiters can be than what they wrap. @@ -77,6 +79,19 @@ impl LayoutMath for LrElem { } } + // Remove weak SpacingFragment immediately after the opening or immediately + // before the closing. + let original_len = fragments.len(); + let mut index = 0; + fragments.retain(|fragment| { + index += 1; + (index != 2 && index + 1 != original_len) + || !matches!( + fragment, + MathFragment::Spacing(SpacingFragment { weak: true, .. }) + ) + }); + ctx.extend(fragments); Ok(()) diff --git a/crates/typst/src/math/mod.rs b/crates/typst/src/math/mod.rs index 10bece1b5..e355cca15 100644 --- a/crates/typst/src/math/mod.rs +++ b/crates/typst/src/math/mod.rs @@ -279,7 +279,10 @@ impl LayoutMath for Content { if let Some(elem) = self.to::() { if let Spacing::Rel(rel) = elem.amount() { if rel.rel.is_zero() { - ctx.push(MathFragment::Spacing(rel.abs.resolve(ctx.styles()))); + ctx.push(SpacingFragment { + width: rel.abs.resolve(ctx.styles()), + weak: elem.weak(ctx.styles()), + }); } } return Ok(()); diff --git a/crates/typst/src/math/row.rs b/crates/typst/src/math/row.rs index 48b339342..4f3e63906 100644 --- a/crates/typst/src/math/row.rs +++ b/crates/typst/src/math/row.rs @@ -10,6 +10,8 @@ use crate::math::{ }; use crate::model::ParElem; +use super::fragment::SpacingFragment; + pub const TIGHT_LEADING: Em = Em::new(0.25); #[derive(Debug, Default, Clone)] @@ -279,8 +281,9 @@ impl MathRow { while let Some(fragment) = iter.next() { if space_is_visible { match fragment { - MathFragment::Space(s) | MathFragment::Spacing(s) => { - items.push(MathParItem::Space(s)); + MathFragment::Space(width) + | MathFragment::Spacing(SpacingFragment { width, .. }) => { + items.push(MathParItem::Space(width)); continue; } _ => {} diff --git a/crates/typst/src/math/spacing.rs b/crates/typst/src/math/spacing.rs index f358aa503..c6c697fe6 100644 --- a/crates/typst/src/math/spacing.rs +++ b/crates/typst/src/math/spacing.rs @@ -2,7 +2,7 @@ use unicode_math_class::MathClass; use crate::foundations::{NativeElement, Scope}; use crate::layout::{Abs, Em, HElem}; -use crate::math::{MathFragment, MathSize}; +use crate::math::{MathFragment, MathSize, SpacingFragment}; pub(super) const THIN: Em = Em::new(1.0 / 6.0); pub(super) const MEDIUM: Em = Em::new(2.0 / 9.0); @@ -28,8 +28,9 @@ pub(super) fn spacing( use MathClass::*; let class = |f: &MathFragment| f.class().unwrap_or(Special); - let resolve = |v: Em, f: &MathFragment| { - Some(MathFragment::Spacing(f.font_size().map_or(Abs::zero(), |size| v.at(size)))) + let resolve = |v: Em, size_ref: &MathFragment| -> Option { + let width = size_ref.font_size().map_or(Abs::zero(), |size| v.at(size)); + Some(SpacingFragment { width, weak: false }.into()) }; let script = |f: &MathFragment| f.style().map_or(false, |s| s.size <= MathSize::Script); diff --git a/tests/ref/math/delimited.png b/tests/ref/math/delimited.png index a89e4917f..6126f58ad 100644 Binary files a/tests/ref/math/delimited.png and b/tests/ref/math/delimited.png differ diff --git a/tests/typ/math/delimited.typ b/tests/typ/math/delimited.typ index ba623b34d..8500aec27 100644 --- a/tests/typ/math/delimited.typ +++ b/tests/typ/math/delimited.typ @@ -51,3 +51,9 @@ $ { x mid(|) sum_(i=1)^oo phi_i (x) < 1 } \ { integral |x| dif x mid(bar.v.double) floor(hat(A) mid(|) { x mid(|) y } mid(|) A) } $ + +--- +// Test ignoring weak spacing immediately after the opening +// and immediately before the closing. + +$ [#h(1em, weak: true)A(dif x, f(x) dif x)sum#h(1em, weak: true)] $