diff --git a/crates/typst-library/src/math/ctx.rs b/crates/typst-library/src/math/ctx.rs index c992c0ebf..59ba057fa 100644 --- a/crates/typst-library/src/math/ctx.rs +++ b/crates/typst-library/src/math/ctx.rs @@ -2,6 +2,7 @@ use ttf_parser::gsub::SubstitutionSubtable; use ttf_parser::math::MathValue; use typst::font::{FontStyle, FontWeight}; use typst::model::realize; +use typst::syntax::is_newline; use unicode_segmentation::UnicodeSegmentation; use super::*; @@ -207,41 +208,64 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> { FrameFragment::new(self, frame).into() } else { // Anything else is handled by Typst's standard text layout. - let spaced = text.graphemes(true).nth(1).is_some(); let mut style = self.style; if self.style.italic == Smart::Auto { style = style.with_italic(false); } let text: EcoString = text.chars().map(|c| style.styled_char(c)).collect(); - let text = TextElem::packed(text) - .styled(TextElem::set_top_edge(TopEdge::Metric(TopEdgeMetric::Bounds))) - .styled(TextElem::set_bottom_edge(BottomEdge::Metric( - BottomEdgeMetric::Bounds, - ))) - .spanned(span); - let par = ParElem::new(vec![text]); - - // There isn't a natural width for a paragraph in a math environment; - // because it will be placed somewhere probably not at the left margin - // it will overflow. So emulate an `hbox` instead and allow the paragraph - // to extend as far as needed. - let frame = par - .layout( - self.vt, - self.outer.chain(&self.local), - false, - Size::splat(Abs::inf()), - false, - )? - .into_frame(); - FrameFragment::new(self, frame) - .with_class(MathClass::Alphabetic) - .with_spaced(spaced) - .into() + if text.contains(is_newline) { + let mut fragments = vec![]; + for (i, piece) in text.split(is_newline).enumerate() { + if i != 0 { + fragments.push(MathFragment::Linebreak); + } + if !piece.is_empty() { + fragments.push(self.layout_complex_text(piece, span)?.into()); + } + } + let mut frame = MathRow::new(fragments).into_frame(self); + let axis = scaled!(self, axis_height); + frame.set_baseline(frame.height() / 2.0 + axis); + FrameFragment::new(self, frame).into() + } else { + self.layout_complex_text(&text, span)?.into() + } }; Ok(fragment) } + pub fn layout_complex_text( + &mut self, + text: &str, + span: Span, + ) -> SourceResult { + let spaced = text.graphemes(true).nth(1).is_some(); + let elem = TextElem::packed(text) + .styled(TextElem::set_top_edge(TopEdge::Metric(TopEdgeMetric::Bounds))) + .styled(TextElem::set_bottom_edge(BottomEdge::Metric( + BottomEdgeMetric::Bounds, + ))) + .spanned(span); + + // There isn't a natural width for a paragraph in a math environment; + // because it will be placed somewhere probably not at the left margin + // it will overflow. So emulate an `hbox` instead and allow the paragraph + // to extend as far as needed. + let frame = ParElem::new(vec![elem]) + .layout( + self.vt, + self.outer.chain(&self.local), + false, + Size::splat(Abs::inf()), + false, + )? + .into_frame(); + + Ok(FrameFragment::new(self, frame) + .with_class(MathClass::Alphabetic) + .with_spaced(spaced)) + } + pub fn styles(&self) -> StyleChain { self.outer.chain(&self.local) } diff --git a/tests/ref/bugs/math-text-break.png b/tests/ref/bugs/math-text-break.png new file mode 100644 index 000000000..a9842f250 Binary files /dev/null and b/tests/ref/bugs/math-text-break.png differ diff --git a/tests/typ/bugs/math-text-break.typ b/tests/typ/bugs/math-text-break.typ new file mode 100644 index 000000000..a8aa1d0a6 --- /dev/null +++ b/tests/typ/bugs/math-text-break.typ @@ -0,0 +1,4 @@ +// Test text with linebreaks in math. + +--- +$ x := "a\nb\nc\nd\ne" $ diff --git a/tests/typ/math/content.typ b/tests/typ/math/content.typ index 49f39733f..e04ebb305 100644 --- a/tests/typ/math/content.typ +++ b/tests/typ/math/content.typ @@ -21,11 +21,11 @@ $#here[f] := #here[Hi there]$. --- // Test boxes without a baseline act as if the baseline is at the base #{ - box(stroke: 0.2pt, $a #box(stroke: 0.2pt, $a$)$) - h(12pt) - box(stroke: 0.2pt, $a #box(stroke: 0.2pt, $g$)$) - h(12pt) - box(stroke: 0.2pt, $g #box(stroke: 0.2pt, $g$)$) + box(stroke: 0.2pt, $a #box(stroke: 0.2pt, $a$)$) + h(12pt) + box(stroke: 0.2pt, $a #box(stroke: 0.2pt, $g$)$) + h(12pt) + box(stroke: 0.2pt, $g #box(stroke: 0.2pt, $g$)$) } ---