From 0b31f6039f7529c6d1f47cd4f2593cd997157470 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 26 Aug 2024 14:49:24 +0000 Subject: [PATCH] Fix nested attachments when the base in `math.attach` has attachments (#4832) --- crates/typst/src/math/attach.rs | 53 ++++++++++++++++++++++---- tests/ref/math-attach-nested-base.png | Bin 0 -> 1062 bytes tests/suite/math/attach.typ | 19 +++++++-- 3 files changed, 61 insertions(+), 11 deletions(-) create mode 100644 tests/ref/math-attach-nested-base.png diff --git a/crates/typst/src/math/attach.rs b/crates/typst/src/math/attach.rs index 035b78125..70bf2e5e9 100644 --- a/crates/typst/src/math/attach.rs +++ b/crates/typst/src/math/attach.rs @@ -52,20 +52,22 @@ pub struct AttachElem { impl LayoutMath for Packed { #[typst_macros::time(name = "math.attach", span = self.span())] fn layout_math(&self, ctx: &mut MathContext, styles: StyleChain) -> SourceResult<()> { - let base = ctx.layout_into_fragment(self.base(), styles)?; + let new_elem = merge_base(self); + let elem = new_elem.as_ref().unwrap_or(self); + let base = ctx.layout_into_fragment(elem.base(), styles)?; let sup_style = style_for_superscript(styles); let sup_style_chain = styles.chain(&sup_style); - let tl = self.tl(sup_style_chain); - let tr = self.tr(sup_style_chain); + let tl = elem.tl(sup_style_chain); + let tr = elem.tr(sup_style_chain); let primed = tr.as_ref().is_some_and(|content| content.is::()); - let t = self.t(sup_style_chain); + let t = elem.t(sup_style_chain); let sub_style = style_for_subscript(styles); let sub_style_chain = styles.chain(&sub_style); - let bl = self.bl(sub_style_chain); - let br = self.br(sub_style_chain); - let b = self.b(sub_style_chain); + let bl = elem.bl(sub_style_chain); + let br = elem.br(sub_style_chain); + let b = elem.b(sub_style_chain); let limits = base.limits().active(styles); let (t, tr) = match (t, tr) { @@ -248,6 +250,43 @@ impl Limits { } } +/// If an AttachElem's base is also an AttachElem, merge attachments into the +/// base AttachElem where possible. +fn merge_base(elem: &Packed) -> Option> { + // Extract from an EquationElem. + let mut base = elem.base(); + if let Some(equation) = base.to_packed::() { + base = equation.body(); + } + + // Move attachments from elem into base where possible. + if let Some(base) = base.to_packed::() { + let mut elem = elem.clone(); + let mut base = base.clone(); + + macro_rules! merge { + ($content:ident) => { + if base.$content.is_none() && elem.$content.is_some() { + base.$content = elem.$content.clone(); + elem.$content = None; + } + }; + } + + merge!(t); + merge!(b); + merge!(tl); + merge!(tr); + merge!(bl); + merge!(br); + + elem.base = base.pack(); + return Some(elem); + } + + None +} + macro_rules! measure { ($e: ident, $attr: ident) => { $e.as_ref().map(|e| e.$attr()).unwrap_or_default() diff --git a/tests/ref/math-attach-nested-base.png b/tests/ref/math-attach-nested-base.png new file mode 100644 index 0000000000000000000000000000000000000000..657cf46f6504fb89fd47cf9b039d0c29ee3de5f2 GIT binary patch literal 1062 zcmV+>1ljwEP)TjWHflL4-&`+mMu6LYsGlwM*u(7MuV#YmtaG8+nH{6^{{p|vo6P$> z?|xvB@Ab5lzxKc-X2^1?0h38|qZ_9gdT<+v-N++< zG2^Zp@cgEBf%{EOd7x}{J&0Ws`~%CV0k^e$Wh1st5B;L*l_}T-j=6JJh;8oCXG1E& z^I11ua?YRop1;<;rcgzA_XUzbzWr-P)q|g3xs!Ga`G!(a58ikE(5@CHFU&%0({E@_O^pq|5ljFO~7=Te7iqD z^=9)QwVX6OxB3jBws#T$x8^MF^Ji}Tvy1>=2NMC0d`bqAA~kJE4)tN0Tdx8@c~KW) z-FsZ>!qV!0)o8D7xLBhJ9O)rV6PRY}F>PQ1`fE{#by$aW_+JW7*F>~|=XL}W28W3* z=N=qZ8=f)A!zGHt3#J-pAjG|Fr_{MA}2Uc z2m(N|gEuU*-n;VvK|%OL5F)?_on#CSC;`{zz|&`OTU>zOXe`=dG){p1Wms1MViiW? z>qcV_EI$IdyTJNG+;F`K-{WCEra*GkL-|BrZ~;h;dNCQYtsGu9jmHc7YRnx+>*fmB zxfl&|+I;@Y&xBZ**7mlJ1MW<0Re1Ws@xX{&0q+I?@Q)#zov7aAa>GjX&D8i|cKZ+T z_xs|fN>{|?-K+o{?)C8xns5L|oZ1YDJ=F&_ftQ<`d-Zv&!~Zcj;?V{Unk?@r0f&*z z56I`g8rrA;Jg^11+AUdfv?Ff}IB8b^CL6gfv27Ze%7@@o+i4|X-w4r-9YL%=7G5eY zZj2B1&5N*f*&}oZ7_aQTEnW7wEyD5tPNvG17;LK1kQ@Vobe z!?#MSyz#=L>*19Xi1`m13X=_nF<5vKUOf(CWd_4qgW((CsX5*%nFWX!K3WP-Omd~E zO7g{B+hKzRySYxXbGe^^g2N~Q50?b2FAmX2k_2AN3H7eC3Ar3{M8j+shO-*nO2D|f zmYmn%=3W5s)B?bQ)w9q?8s*;tfDr)vol^mul6SSEZXB6*Q0i25NJP@pGm)wNa-BXF gby$aW`2Pg|26L$_TsK|1kpKVy07*qoM6N<$f)Q=^O#lD@ literal 0 HcmV?d00001 diff --git a/tests/suite/math/attach.typ b/tests/suite/math/attach.typ index c5ca57357..ff859c0f1 100644 --- a/tests/suite/math/attach.typ +++ b/tests/suite/math/attach.typ @@ -69,7 +69,7 @@ $ sqrt(a_(1/2)^zeta), sqrt(a_alpha^(1/2)), sqrt(a_(1/2)^(3/4)) \ sqrt(attach(a, tl: 1/2, bl: 3/4, tr: 1/2, br: 3/4)) $ --- math-attach-descender-collision --- -// Test for no collisions between descenders/ascenders and attachments +// Test for no collisions between descenders/ascenders and attachments. $ sup_(x in P_i) quad inf_(x in P_i) $ $ op("fff",limits: #true)^(y) quad op("yyy", limits:#true)_(f) $ @@ -112,7 +112,7 @@ $ attach(A, t: #context oops) $ $iota_a^b$ --- math-attach-default-placement --- -// Test default of limit attachments on relations at all sizes +// Test default of limit attachments on relations at all sizes. #set page(width: auto) $ a =^"def" b quad a lt.eq_"really" b quad a arrow.r.long.squiggly^"slowly" b $ $a =^"def" b quad a lt.eq_"really" b quad a arrow.r.long.squiggly^"slowly" b$ @@ -120,11 +120,22 @@ $a =^"def" b quad a lt.eq_"really" b quad a arrow.r.long.squiggly^"slowly" b$ $a scripts(=)^"def" b quad a scripts(lt.eq)_"really" b quad a scripts(arrow.r.long.squiggly)^"slowly" b$ --- math-attach-integral --- -// Test default of scripts attachments on integrals at display size +// Test default of scripts attachments on integrals at display size. $ integral.sect_a^b quad \u{2a1b}_a^b quad limits(\u{2a1b})_a^b $ $integral.sect_a^b quad \u{2a1b}_a^b quad limits(\u{2a1b})_a^b$ --- math-attach-large-operator --- -// Test default of limit attachments on large operators at display size only +// Test default of limit attachments on large operators at display size only. $ tack.t.big_0^1 quad \u{02A0A}_0^1 quad join_0^1 $ $tack.t.big_0^1 quad \u{02A0A}_0^1 quad join_0^1$ + +--- math-attach-nested-base --- +// Test attachments when the base has attachments. +$ attach(a^b, b: c) quad + attach(attach(attach(attach(attach(attach(sum, tl: 1), t: 2), tr: 3), br: 4), b: 5), bl: 6) $ + +#let a0 = math.attach(math.alpha, b: [0]) +#let a1 = $alpha^1$ + +$ a0 + a1 + a0_2 \ + a1_2 + a0^2 + a1^2 $