From 45ad06453b8d2309903ec8586f2dbed1d220a818 Mon Sep 17 00:00:00 2001 From: Siddhant Agarwal <68201519+siddhantdev@users.noreply.github.com> Date: Mon, 1 Sep 2025 14:22:06 +0530 Subject: [PATCH] Allow augment line at the beginning and end of a matrix (#5806) Co-authored-by: Laurenz --- crates/typst-layout/src/math/mat.rs | 68 +++++++++++++++++++--------- tests/ref/math-mat-augment.png | Bin 3563 -> 10359 bytes tests/suite/math/mat.typ | 17 ++++--- 3 files changed, 57 insertions(+), 28 deletions(-) diff --git a/crates/typst-layout/src/math/mat.rs b/crates/typst-layout/src/math/mat.rs index 97c160cf..a1ba5fc2 100644 --- a/crates/typst-layout/src/math/mat.rs +++ b/crates/typst-layout/src/math/mat.rs @@ -82,28 +82,27 @@ pub fn layout_mat( ) -> SourceResult<()> { let span = elem.span(); let rows = &elem.rows; + let nrows = rows.len(); let ncols = rows.first().map_or(0, |row| row.len()); let augment = elem.augment.resolve(styles); if let Some(aug) = &augment { for &offset in &aug.hline.0 { - if offset == 0 || offset.unsigned_abs() >= rows.len() { + if offset > nrows as isize || offset.unsigned_abs() > nrows { bail!( span, - "cannot draw a horizontal line after row {} of a matrix with {} rows", - if offset < 0 { rows.len() as isize + offset } else { offset }, - rows.len() + "cannot draw a horizontal line at offset {offset} \ + in a matrix with {nrows} rows", ); } } for &offset in &aug.vline.0 { - if offset == 0 || offset.unsigned_abs() >= ncols { + if offset > ncols as isize || offset.unsigned_abs() > ncols { bail!( span, - "cannot draw a vertical line after column {} of a matrix with {} columns", - if offset < 0 { ncols as isize + offset } else { offset }, - ncols + "cannot draw a vertical line at offset {offset} \ + in a matrix with {ncols} columns", ); } } @@ -165,7 +164,7 @@ fn layout_body( ..Default::default() }; - let (hline, vline, stroke) = match augment { + let (mut hline, mut vline, stroke) = match augment { Some(augment) => { // We need to get stroke here for ownership. let stroke = augment.stroke.unwrap_or_default().unwrap_or(default_stroke); @@ -214,20 +213,48 @@ fn layout_body( } } + for line in hline.0.iter_mut() { + if *line < 0 { + *line += nrows as isize; + } + } + + for line in vline.0.iter_mut() { + if *line < 0 { + *line += ncols as isize; + } + } + // For each row, combine maximum ascent and descent into a row height. // Sum the row heights, then add the total height of the gaps between rows. - let total_height = + let mut total_height = heights.iter().map(|&(a, b)| a + b).sum::() + gap.y * (nrows - 1) as f64; + if hline.0.contains(&0) { + total_height += gap.y; + } + + if hline.0.contains(&(nrows as isize)) { + total_height += gap.y; + } + // Width starts at zero because it can't be calculated until later let mut frame = Frame::soft(Size::new(Abs::zero(), total_height)); let mut x = Abs::zero(); + if vline.0.contains(&0) { + frame.push( + Point::with_x(x + half_gap.x), + line_item(total_height, true, stroke.clone(), span), + ); + x += gap.x; + } + for (index, col) in cols.into_iter().enumerate() { let AlignmentResult { points, width: rcol } = alignments(&col); - let mut y = Abs::zero(); + let mut y = if hline.0.contains(&0) { gap.y } else { Abs::zero() }; for (cell, &(ascent, descent)) in col.into_iter().zip(&heights) { let cell = cell.into_line_frame(&points, alternator); @@ -249,9 +276,7 @@ fn layout_body( x += rcol; // If a vertical line should be inserted after this column - if vline.0.contains(&(index as isize + 1)) - || vline.0.contains(&(1 - ((ncols - index) as isize))) - { + if vline.0.contains(&(index as isize + 1)) { frame.push( Point::with_x(x + half_gap.x), line_item(total_height, true, stroke.clone(), span), @@ -262,16 +287,17 @@ fn layout_body( x += gap.x; } - // Once all the columns are laid out, the total width can be calculated - let total_width = x - gap.x; + let total_width = if !(vline.0.contains(&(ncols as isize))) { x - gap.x } else { x }; // This allows the horizontal lines to be laid out for line in hline.0 { - let real_line = - if line < 0 { nrows - line.unsigned_abs() } else { line as usize }; - let offset = (heights[0..real_line].iter().map(|&(a, b)| a + b).sum::() - + gap.y * (real_line - 1) as f64) - + half_gap.y; + let offset = if line == 0 { + gap.y + } else { + (heights[0..line as usize].iter().map(|&(a, b)| a + b).sum::() + + gap.y * (line - 1) as f64) + + half_gap.y + }; frame.push( Point::with_y(offset), diff --git a/tests/ref/math-mat-augment.png b/tests/ref/math-mat-augment.png index 306c4b1995f82e367be5442de9253dd9ae69e78e..3845693dd04dad75562f16490a04c55ec846b5f4 100644 GIT binary patch literal 10359 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 Sjh6?7aEh|3GBr}>!T%4Lz{{lo literal 3563 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