From 68b365b35197dd2f7aca04f7fa9d712f7cee621d Mon Sep 17 00:00:00 2001 From: Laurenz Date: Wed, 13 Sep 2023 13:45:05 +0200 Subject: [PATCH] Fix newline in text in math Fixes #1948 --- crates/typst-library/src/math/ctx.rs | 76 ++++++++++++++++++--------- tests/ref/bugs/math-text-break.png | Bin 0 -> 900 bytes tests/typ/bugs/math-text-break.typ | 4 ++ tests/typ/math/content.typ | 10 ++-- 4 files changed, 59 insertions(+), 31 deletions(-) create mode 100644 tests/ref/bugs/math-text-break.png create mode 100644 tests/typ/bugs/math-text-break.typ 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 0000000000000000000000000000000000000000..a9842f2500536a0b94440d0b52aca06d41dcc251 GIT binary patch literal 900 zcmeAS@N?(olHy`uVBq!ia0y~yU}OQZS8xCc2IW)dzcVl}dwaS#hE&{od)M1zN}$BC zkIhjRcqesCSad?-gom~cPn3qIROXasucb4~KF(rMYI)0|>v}RedHP0!?ii05+`1DC z4)*;~iR<-eT&o=Wxa zQEp~`l3DW&&-dgc^p1}MO^eHRa>P$ z9IuZ>I(yE}ZcA2l3QbT`_ddO4!{-ZT+8Yul%L#6hV@?nA=THA)DHf#{m7u$CRjKdp zn12h57))#~D<7(6uya#43tKh2%1*-lZr9u!p=-Oo^nCEx%gna5{ON9M!$K1m?PJd( z-j{l_Hk|X6RSMarTsT$snnJFkTPWlD*;}K;%a-lpXXKcFP2Op?m;AjQd?#vFtUa^m z@7);%w{|bH+ZNc~-~84g>4`&5OunFK0lma7q7*2=8@pO z)U|hyA7^y$V36o^V7U^n`AV?QKt$%-qvTIwH#rn{KV#VKXmY4Hl!Kvhk>I*jSxQe< zM=9BaZf8i?|EAecLE=Em@wkf|wRPgV;^n@_%>3*6X@g1lmJ`YvvDSl6zlTizkozCC}~G8 ZnJ*QQQ7JO@w;?EddAj