From 2d63b89a0a6087ece437e99d24081af63a0af092 Mon Sep 17 00:00:00 2001 From: mkorje Date: Fri, 11 Oct 2024 18:08:21 +1100 Subject: [PATCH] Add `column-gap` option to `math.equation` --- crates/typst-layout/src/math/mat.rs | 2 +- crates/typst-layout/src/math/run.rs | 4 ++- crates/typst-layout/src/math/shared.rs | 7 +++-- crates/typst-library/src/math/equation.rs | 18 +++++++++++-- tests/ref/math-align-columns.png | Bin 0 -> 1407 bytes tests/ref/math-equation-align-column-gap.png | Bin 0 -> 489 bytes tests/ref/math-equation-align-columns-1.png | Bin 0 -> 1950 bytes tests/ref/math-equation-align-columns-2.png | Bin 0 -> 771 bytes tests/ref/math-multiline-line-spacing.png | Bin 0 -> 474 bytes tests/suite/math/alignment.typ | 1 + tests/suite/math/call.typ | 1 + tests/suite/math/equation.typ | 26 +++++++++++++++++++ tests/suite/math/multiline.typ | 18 ++++++++++++- 13 files changed, 70 insertions(+), 7 deletions(-) create mode 100644 tests/ref/math-align-columns.png create mode 100644 tests/ref/math-equation-align-column-gap.png create mode 100644 tests/ref/math-equation-align-columns-1.png create mode 100644 tests/ref/math-equation-align-columns-2.png create mode 100644 tests/ref/math-multiline-line-spacing.png diff --git a/crates/typst-layout/src/math/mat.rs b/crates/typst-layout/src/math/mat.rs index d678f8658..1056735a7 100644 --- a/crates/typst-layout/src/math/mat.rs +++ b/crates/typst-layout/src/math/mat.rs @@ -220,7 +220,7 @@ fn layout_body( let mut x = Abs::zero(); for (index, col) in cols.into_iter().enumerate() { - let AlignmentResult { points, width: rcol } = alignments(&col); + let AlignmentResult { points, width: rcol } = alignments(&col, Abs::zero()); let mut y = Abs::zero(); diff --git a/crates/typst-layout/src/math/run.rs b/crates/typst-layout/src/math/run.rs index ae64368d6..faee3797a 100644 --- a/crates/typst-layout/src/math/run.rs +++ b/crates/typst-layout/src/math/run.rs @@ -192,7 +192,9 @@ impl MathRun { pub fn multiline_frame_builder(self, styles: StyleChain) -> MathRunFrameBuilder { let rows: Vec<_> = self.rows(); let row_count = rows.len(); - let alignments = alignments(&rows); + + let column_gap = EquationElem::column_gap_in(styles).resolve(styles); + let alignments = alignments(&rows, column_gap); let leading = if EquationElem::size_in(styles) >= MathSize::Text { ParElem::leading_in(styles) diff --git a/crates/typst-layout/src/math/shared.rs b/crates/typst-layout/src/math/shared.rs index 600c130d4..8b471376b 100644 --- a/crates/typst-layout/src/math/shared.rs +++ b/crates/typst-layout/src/math/shared.rs @@ -118,7 +118,7 @@ pub fn stack( baseline: usize, alternator: LeftRightAlternator, ) -> Frame { - let AlignmentResult { points, width } = alignments(&rows); + let AlignmentResult { points, width } = alignments(&rows, Abs::zero()); let rows: Vec<_> = rows .into_iter() .map(|row| row.into_line_frame(&points, alternator)) @@ -149,7 +149,7 @@ pub fn stack( } /// Determine the positions of the alignment points, according to the input rows combined. -pub fn alignments(rows: &[MathRun]) -> AlignmentResult { +pub fn alignments(rows: &[MathRun], gap: Abs) -> AlignmentResult { let mut widths = Vec::::new(); let mut pending_width = Abs::zero(); @@ -159,6 +159,9 @@ pub fn alignments(rows: &[MathRun]) -> AlignmentResult { for fragment in row.iter() { if matches!(fragment, MathFragment::Align) { + if alignment_index > 0 && alignment_index % 2 == 0 { + width += gap; + } if alignment_index < widths.len() { widths[alignment_index].set_max(width); } else { diff --git a/crates/typst-library/src/math/equation.rs b/crates/typst-library/src/math/equation.rs index 32be216a4..8195ded2f 100644 --- a/crates/typst-library/src/math/equation.rs +++ b/crates/typst-library/src/math/equation.rs @@ -11,13 +11,15 @@ use crate::foundations::{ }; use crate::introspection::{Count, Counter, CounterUpdate, Locatable}; use crate::layout::{ - AlignElem, Alignment, BlockElem, InlineElem, OuterHAlignment, SpecificAlignment, - VAlignment, + AlignElem, Alignment, BlockElem, Em, InlineElem, Length, OuterHAlignment, + SpecificAlignment, VAlignment, }; use crate::math::{MathSize, MathVariant}; use crate::model::{Numbering, Outlinable, ParLine, Refable, Supplement}; use crate::text::{FontFamily, FontList, FontWeight, LocalName, TextElem}; +const DEFAULT_COL_GAP: Em = Em::new(1.5); + /// A mathematical equation. /// /// Can be displayed inline with text or as a separate block. An equation @@ -101,6 +103,17 @@ pub struct EquationElem { /// ``` pub supplement: Smart>, + /// The gap between columns. + /// + /// ```example + /// #set math.equation(column-gap: 3em) + /// $ 4 &= 4 & &"yes" \ + /// 0 &= 0 & &"no" \ + /// 1+1 &= 2 & &"maybe" $ + /// ``` + #[default(DEFAULT_COL_GAP.into())] + pub column_gap: Length, + /// The contents of the equation. #[required] pub body: Content, @@ -185,6 +198,7 @@ impl Show for Packed { impl ShowSet for Packed { fn show_set(&self, styles: StyleChain) -> Styles { let mut out = Styles::new(); + out.set(EquationElem::set_column_gap(self.column_gap(styles))); if self.block(styles) { out.set(AlignElem::set_alignment(Alignment::CENTER)); out.set(BlockElem::set_breakable(false)); diff --git a/tests/ref/math-align-columns.png b/tests/ref/math-align-columns.png new file mode 100644 index 0000000000000000000000000000000000000000..8420ae315385954c72a71fc503728ae024bb8062 GIT binary patch literal 1407 zcmV-_1%UdAP)NklrYd69LI70hQ)2!GN+p{L#A=E;O2D8Hl3o;s7#&2TNn^05>(JA^OE3(sN6*5S_&1F zc|~zm7yf(_g5oP>U}chcq0;dk?}FH1I`QAxa1&V{RnP*A$Bkp>fCT%IAM1h68G0a^eIHL zmjOgLFMRV^R5(CR#ISS#Vz&IgCI+A?VpyaJ@W^rQZ`|*z^bJH}Wfc~;=ZH)+0FBNI zr${NZH@wh+nAPw?i?hNVp4;B7X#M<`zDiZ=OOQ)+(4ak>E4$EOVEQeoZ zc<(G3qfo5v`vRt{m$e|VF3<=4PO38 zE!4q#7I3l9^eJ?>L*9BQT@NRJMMU>i7hH~XAe?w-6%uAw22LIZ%~HDdo`>r}JpZu- zjw;Z2HX^bx52$}+f4H=w_H%&85yK*l07V|^aPMwV_^EdX4?%F~b)VzF($p)fuL0Z; z#^-?cHyr$PSwoZwlaUBJ4WZGnp$yjDggKosyAF#h483h;q_X({#rB8Kw8KQ{ix=tO zyBu(}&^Pljo$k;ut(US5Q0y*#anDQ(6qMN=PFm2;jZZQVQO#5)?YWNm$b4l&PzkL; zE0l3NHPmkQQO53WLG0Gnv*|^z=YaIdCsPjpx5B3rm8x1K8dSr27?aB3t=jIedLuo1 zE+Db)s9mq(clb*!JaGdIg@Qon9?u>Os%0Cyrt;Lo@kwAFWIKm3^TL2gYg8TeLsLsZw z;uWAzaX5VDXTY@+u|@YyB^}SPDLjS>svQpd`~gGnTA(LF@DyP1&E%C2VKjxXxedVL zkXgJ?U*~wZVJ?`~Xb`{na5R|XZNIo=IpA=(Imojhbw@Q~ z_h-cxT=1yFmS>y1o{b~iuhnWhkTPgrXvU_lrp9jha>-#iEQeo~e*w_?DYj-ii$MSY N002ovPDHLkV1iq5mEiyY literal 0 HcmV?d00001 diff --git a/tests/ref/math-equation-align-column-gap.png b/tests/ref/math-equation-align-column-gap.png new file mode 100644 index 0000000000000000000000000000000000000000..696793129838f29c4e5722abc53c47c3a26abb82 GIT binary patch literal 489 zcmV98`Nk2ggUDy^%cWF1h(M1Vew58C3V4;haUKc_afs_b^P$q<8i_oe;1&s=c z2Jw;zDtKi?yhKD4Q8Xf=lZnnFN8Fs%IZPBm&hHC6yzug`;DS_`unC*637=>9UyyQz z-unrsYAIK&Ssl(N<~$@{>w7))j%YJcQ0Cq&E zEdq_!-gT7W;23w2IFGTSXuFZeLvppaP^}K%@h0C*OBb(vR;qc5KB$RG)~fBwE;2E>F-_*8Bj|Bkws{f6zDD*W@_^!W{k)0E@BqZ8h@ zlH)+JAnYR8H?0!74x;scfl}cFfP1YaB9N`( z)3h?YeYL29 z5h!Dy^jte6hvTDL73Rj`2akdCt?2(40E+!jK&fQSR&c9I@DYHvMLYVT&;=KFa2H*X zeMM>R-sg{ldlko*iC6ZD5-2n!9o&q*`cJ{su1@ZSGHboS{Sis{+*z{=5>G7IWq_z~ zMFo_|4uixGfo1&X7_k{jT%BbFwb=u?KuS6^X{*& zL1H(S)IwSAN?+-(xd};yjyZ_owRrYQKRIx1IwlFj6Xw1)Mr*a&_tfv-YHl^Rn)_blUK2W?6?DlC@2f#}$l-Z9CAaUW z{EjL=G$2R|4Zi5>JIrHeZ{OmSiFk2&rIQljQF+V?{kKgBkixVx7oWT%gkNp^A z?!z?>&*gK&4TB|WQs7kghL9$3^6x7~FNK3?;PQj>F;qe>2D$ah>)qgn$$e0N1vql> zP$?L%G4&WwueX3PuZ_42IL}?3a~h~KR^@<+xIYh)rr8Bu03hrPJ_tC^ecez9wCiiP z+5q6>Lkr=caKCKs=u*rzrKM;7*$SrP#drI_6@^(|1%ND{8-PsieSaaOvO2hKf%snd z_!$$pErI<$0Iq&=0+7jVo`a(+1^1rmEdaA#yBm@Prz4Nc<$l?Q=!Cd^`RgI7d>|Fv zL`$1D0Q6_BJS>-c<|*VaERJlQ2T|+T0B}An)(HS`a#fm5CU@#;;Dx>~F|!w{1){E*?cio%&vNmq z58c%5G`D^cI>$q|vknji6oQLMhtT0Pcfl)_72(?!^%s8P6Ws&G;?V&~SV2ID^!ZU% zgdwV4(>DiZ&A(}y{M~93nEu%fkRNUm2iCdkrn%}pw1M_h{3*cE~6t!BAOPsg|soVx~X8=5d? z*0J|%SN=Fz>kTsNq`+7ve6jmOE>=SB_|O3+&}BHhKLxTHhvymU?+t2hHMg2u&8_BE zbE~-t^%bG7A{12Q+=Tjy(pQlSDr#<8eMRr9=mix$H@Utd_f>>~ijbRJUy=JNLP2$R zxykhvxv!Ebs79EZTwhW9Dw%?6gt^J}6}hjHDX2!6n_OQ}`zpDDYNWZz^%b?Rk}0T0 znwwl-QTr;Hf@-9>$@LYruaYUKMwXjgUs3xinSzRtn^0d-`YLikMa@m9uPA*LwV+bJ zf2+CG+-mN7mYXmepzH*w!vJz_`fPx{6QB=jpb%+MJL5Y(SFjUcxy${~HQDM4 z*W1Gk18BKlup>G)KSXy**=)cKpVh!P@5-E{Yy~?3mOCjFcn;t13YQ-S&~s14&&PyW zXETeN1ZA^p`l+NQ8F8- zipqf@B@cPaBEC;kuoEz^i?i2k-`Ll>Soh09nPC7uw`74GC8?CzfPW{hEhFp%vJ4P@ z7;YFq&wYK*-UFoBfHixs!A98$lw@bM$_xVtxe2oY!cKrX44~#F%myes0qQV7{T{34 kR&%Sl)!b@sHTTHjA2GG;u`!CnApigX07*qoM6N<$f<>sa&;S4c literal 0 HcmV?d00001 diff --git a/tests/ref/math-equation-align-columns-2.png b/tests/ref/math-equation-align-columns-2.png new file mode 100644 index 0000000000000000000000000000000000000000..edf9862855b14627d0082ec6e775a081cdbc50c3 GIT binary patch literal 771 zcmV+e1N{7nP)R-*v0008YNklSM1{EP)1a`p6=)zD_L+#|QAzP4&7U*V*=QPvUEkWB> znLL*!YHVg=tEKCKwgYaNt23aJA znr>VZzN@4g_Zn8*l?`Er#R(2kOE<9c4o`tl$in1@E-Ag;(r=koOnl`dI0&e^ZX5;g zzFH@O3534N_Zp`6*O$bboj}1IKO0a{lg54miEX!?2jq0I_XcPT;gllXZuEp{nEsP% zO@EK#I(owi;A0T^#VFjaW0MSpCR32fWCb>jB5RmFChzlhqPRMT$Sx;<+8dneaw)DJ zIo=(jxcUxoX_$UBGql`>Qc}&fF#?!0R310`6r=j0_%Dj|Oq-=%!*pb1lzN+n&aos` z?Y}VUT2phz(SY{pf}XCY>*;#>V$%mv_{5-(dAL6?kDQmNXfkV(UYLmBnnGB=(a1cS zONxS~nNwCND!vA5kvf6wSy8*^i=S~ z64TE}|EP$K$&>sN%OoKqAw$YG3aHV^9O0=9=Zqq)^-2@002ovPDHLkV1jHw Bc1!>O literal 0 HcmV?d00001 diff --git a/tests/ref/math-multiline-line-spacing.png b/tests/ref/math-multiline-line-spacing.png new file mode 100644 index 0000000000000000000000000000000000000000..468e92f2b2594d8a26281d90b4cfb17d11f21034 GIT binary patch literal 474 zcmV<00VV#4P)F`k!HK* zpp=b|<3$qG<!iIx-4LwO-J@Fa1XKwS_YJ~DwK{X%T4c}q-E-p(#?MPg%PjF+dxhpQ`o zrGrEfkAbDUqWN;WPLLa$Tjj?Cf?*6}7{eIGForSw|H3|9vb7W+>wH*#$F`R86Vk%X z>#PvP`g4@VU6q?8aa$u*3E{dfyFor-09=a|s>|f34v8On&{$bULfAeR3=5g%gwpnm zpemAR05UTY!rFV_nu3kh&y=a*7G1f z43$>_s8yzEi~FzzNb=4@qtMaMX_d)RtfV|Q{Qd!{0X=mVZ#9N7jNvZM7r24Y*)q8{ Q=>Px#07*qoM6N<$f`KK?djJ3c literal 0 HcmV?d00001 diff --git a/tests/suite/math/alignment.typ b/tests/suite/math/alignment.typ index 941c20556..521886cc9 100644 --- a/tests/suite/math/alignment.typ +++ b/tests/suite/math/alignment.typ @@ -28,6 +28,7 @@ $ --- math-align-toggle --- // Test #460 equations. +#set math.equation(column-gap: 0em) $ a &=b & quad c&=d \ e &=f & g&=h diff --git a/tests/suite/math/call.typ b/tests/suite/math/call.typ index 5caacfac6..116168a11 100644 --- a/tests/suite/math/call.typ +++ b/tests/suite/math/call.typ @@ -205,6 +205,7 @@ $ sin(#1) $ // attach, etc.) // // This is not good, so this test should fail and be updated once it is fixed. +#set math.equation(column-gap: 0em) #let id(body) = body #let bx(body) = box(body, stroke: blue+0.5pt, inset: (x:2pt, y:3pt)) #let eq(body) = math.equation(body) diff --git a/tests/suite/math/equation.typ b/tests/suite/math/equation.typ index 148a49d02..c379e5287 100644 --- a/tests/suite/math/equation.typ +++ b/tests/suite/math/equation.typ @@ -54,6 +54,32 @@ This is big: $sum_(i=0)^n$ #eq(start) #eq(end) +--- math-equation-align-columns-1 --- +// Test columns in equations. +#set page(width: auto) +$ sum a &<= & sum b &<= & &sum c \ + log sum a &<= & log sum b &<= & log &sum c $ + +#math.equation(block: true, column-gap: 0em, $ + sum a &<= & sum b &<= & &sum c \ + log sum a &<= & log sum b &<= & log &sum c +$) + +--- math-equation-align-columns-2 --- +// Test columns in equations. +#set page(width: auto) +#set math.equation(column-gap: 1em) +#block(stroke: black + 1pt, $ + && x & = y && & a & = b + c && && && \ + && -4 + 5x & = -2 && & a b & = c b && && && +$) + +--- math-equation-align-column-gap --- +// Test column-gap in equations. +#set math.equation(column-gap: 4em) +$ a &=b & c&=d \ + e &=f & g&=h $ + --- math-equation-number-align --- #set math.equation(numbering: "(1)") diff --git a/tests/suite/math/multiline.typ b/tests/suite/math/multiline.typ index 34e66b99c..ea5157219 100644 --- a/tests/suite/math/multiline.typ +++ b/tests/suite/math/multiline.typ @@ -17,6 +17,15 @@ $ x + 1 &= a^2 + b^2 \ $ a + b &= 2 + 3 &= 5 \ b &= c &= 3 $ +--- math-align-columns --- +// Test columns created with alignment points. +$ A &= B &= C \ + D &= E &= F $ +$ A &= B B B B &= C \ + D &= E &= F $ +$ A &= B & &= C \ + D &= E & &= F $ + --- math-align-cases --- // Test in case distinction. $ f := cases( @@ -55,6 +64,12 @@ $ $ Multiple trailing line breaks. +--- math-multiline-line-spacing --- +// Test modifying spacing between lines. +#set par(leading: 2em) +$ a &=b & c&=d \ + e &=f & g&=h $ + --- math-linebreaking-after-binop-and-rel --- // Basic breaking after binop, rel #let hrule(x) = box(line(length: x)) @@ -110,6 +125,7 @@ Nothing: $ $, just empty. --- math-pagebreaking --- // Test breaking of equations at page boundaries. #set page(height: 5em) +#set math.equation(column-gap: 0em) #show math.equation: set block(breakable: true) $ a &+ b + & c \ @@ -121,7 +137,7 @@ $ a &+ b + & c \ --- math-pagebreaking-numbered --- // Test breaking of equations with numbering. #set page(height: 5em) -#set math.equation(numbering: "1") +#set math.equation(column-gap: 0em, numbering: "1") #show math.equation: set block(breakable: true) $ a &+ b + & c \