diff --git a/crates/typst/src/layout/align.rs b/crates/typst/src/layout/align.rs index 957444122..188796923 100644 --- a/crates/typst/src/layout/align.rs +++ b/crates/typst/src/layout/align.rs @@ -416,6 +416,16 @@ impl VAlignment { Self::Bottom => Self::Top, } } + + /// Returns the position of this alignment in a container with the given + /// extent. + pub fn position(self, extent: Abs) -> Abs { + match self { + Self::Top => Abs::zero(), + Self::Horizon => extent / 2.0, + Self::Bottom => extent, + } + } } impl FixAlignment for VAlignment { diff --git a/crates/typst/src/math/fragment.rs b/crates/typst/src/math/fragment.rs index ef865b380..63c42cbce 100644 --- a/crates/typst/src/math/fragment.rs +++ b/crates/typst/src/math/fragment.rs @@ -6,7 +6,9 @@ use ttf_parser::{GlyphId, Rect}; use unicode_math_class::MathClass; use crate::foundations::StyleChain; -use crate::layout::{Abs, Corner, Em, Frame, FrameItem, HideElem, Point, Size}; +use crate::layout::{ + Abs, Corner, Em, Frame, FrameItem, HideElem, Point, Size, VAlignment, +}; use crate::math::{ scaled_font_size, EquationElem, Limits, MathContext, MathSize, Scaled, }; @@ -408,9 +410,15 @@ impl VariantFragment { /// Vertically adjust the fragment's frame so that it is centered /// on the axis. pub fn center_on_axis(&mut self, ctx: &MathContext) { + self.align_on_axis(ctx, VAlignment::Horizon) + } + + /// Vertically adjust the fragment's frame so that it is aligned + /// to the given alignment on the axis. + pub fn align_on_axis(&mut self, ctx: &MathContext, align: VAlignment) { let h = self.frame.height(); let axis = ctx.constants.axis_height().scaled(ctx, self.font_size); - self.frame.set_baseline(h / 2.0 + axis); + self.frame.set_baseline(align.inv().position(h + axis * 2.0)); } } diff --git a/crates/typst/src/math/lr.rs b/crates/typst/src/math/lr.rs index 671aa7df9..e542b8db4 100644 --- a/crates/typst/src/math/lr.rs +++ b/crates/typst/src/math/lr.rs @@ -10,6 +10,8 @@ use crate::math::{ }; use crate::text::TextElem; +use super::delimiter_alignment; + /// How much less high scaled delimiters can be than what they wrap. pub(super) const DELIM_SHORT_FALL: Em = Em::new(0.1); @@ -160,7 +162,7 @@ fn scale( let short_fall = DELIM_SHORT_FALL.at(glyph.font_size); let mut stretched = glyph.stretch_vertical(ctx, height, short_fall); - stretched.center_on_axis(ctx); + stretched.align_on_axis(ctx, delimiter_alignment(stretched.c)); *fragment = MathFragment::Variant(stretched); if let Some(class) = apply { diff --git a/crates/typst/src/math/matrix.rs b/crates/typst/src/math/matrix.rs index 138a494bc..2581cfee1 100644 --- a/crates/typst/src/math/matrix.rs +++ b/crates/typst/src/math/matrix.rs @@ -20,6 +20,8 @@ use crate::text::TextElem; use crate::utils::Numeric; use crate::visualize::{FixedStroke, Geometry, LineCap, Shape, Stroke}; +use super::delimiter_alignment; + const DEFAULT_ROW_GAP: Em = Em::new(0.5); const DEFAULT_COL_GAP: Em = Em::new(0.5); const VERTICAL_PADDING: Ratio = Ratio::new(0.1); @@ -610,7 +612,7 @@ fn layout_delimiters( if let Some(left) = left { let mut left = GlyphFragment::new(ctx, styles, left, span) .stretch_vertical(ctx, target, short_fall); - left.center_on_axis(ctx); + left.align_on_axis(ctx, delimiter_alignment(left.c)); ctx.push(left); } @@ -619,7 +621,7 @@ fn layout_delimiters( if let Some(right) = right { let mut right = GlyphFragment::new(ctx, styles, right, span) .stretch_vertical(ctx, target, short_fall); - right.center_on_axis(ctx); + right.align_on_axis(ctx, delimiter_alignment(right.c)); ctx.push(right); } diff --git a/crates/typst/src/math/mod.rs b/crates/typst/src/math/mod.rs index a97f56dc9..3b493b81a 100644 --- a/crates/typst/src/math/mod.rs +++ b/crates/typst/src/math/mod.rs @@ -46,7 +46,7 @@ use crate::foundations::{ StyledElem, }; use crate::introspection::TagElem; -use crate::layout::{BoxElem, Frame, FrameItem, HElem, Point, Size, Spacing}; +use crate::layout::{BoxElem, Frame, FrameItem, HElem, Point, Size, Spacing, VAlignment}; use crate::realize::{process, BehavedBuilder}; use crate::text::{LinebreakElem, SpaceElem, TextElem}; @@ -317,3 +317,11 @@ impl LayoutMath for Content { Ok(()) } } + +fn delimiter_alignment(delimiter: char) -> VAlignment { + match delimiter { + '\u{231c}' | '\u{231d}' => VAlignment::Top, + '\u{231e}' | '\u{231f}' => VAlignment::Bottom, + _ => VAlignment::Horizon, + } +} diff --git a/tests/ref/issue-4188-lr-corner-brackets.png b/tests/ref/issue-4188-lr-corner-brackets.png new file mode 100644 index 000000000..c932def21 Binary files /dev/null and b/tests/ref/issue-4188-lr-corner-brackets.png differ diff --git a/tests/suite/math/delimited.typ b/tests/suite/math/delimited.typ index 42a67c4ea..632fbd407 100644 --- a/tests/suite/math/delimited.typ +++ b/tests/suite/math/delimited.typ @@ -62,3 +62,7 @@ $ 1/(2 y (x) (2(3)) $ // 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)] $ + +--- issue-4188-lr-corner-brackets --- +// Test positioning of U+231C to U+231F +$⌜a⌟⌞b⌝$ = $⌜$$a$$⌟$$⌞$$b$$⌝$