Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

show math.equation: set align(..) shall not break alignment points #4094

Merged
merged 3 commits into from
May 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions crates/typst/src/math/matrix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ use crate::layout::{
};
use crate::math::{
alignments, scaled_font_size, stack, style_for_denominator, AlignmentResult,
FrameFragment, GlyphFragment, LayoutMath, MathContext, Scaled, DELIM_SHORT_FALL,
FrameFragment, GlyphFragment, LayoutMath, LeftRightAlternator, MathContext, Scaled,
DELIM_SHORT_FALL,
};
use crate::syntax::{Span, Spanned};
use crate::text::TextElem;
Expand Down Expand Up @@ -67,6 +68,7 @@ impl LayoutMath for Packed<VecElem> {
self.children(),
FixedAlignment::Center,
self.gap(styles),
LeftRightAlternator::Right,
)?;

layout_delimiters(
Expand Down Expand Up @@ -324,6 +326,7 @@ impl LayoutMath for Packed<CasesElem> {
self.children(),
FixedAlignment::Start,
self.gap(styles),
LeftRightAlternator::None,
)?;

let (open, close) = if self.reverse(styles) {
Expand Down Expand Up @@ -387,6 +390,7 @@ fn layout_vec_body(
column: &[Content],
align: FixedAlignment,
row_gap: Rel<Abs>,
alternator: LeftRightAlternator,
) -> SourceResult<Frame> {
let gap = row_gap.relative_to(ctx.regions.base().y);

Expand All @@ -396,7 +400,7 @@ fn layout_vec_body(
flat.push(ctx.layout_into_run(child, styles.chain(&denom_style))?);
}

Ok(stack(flat, align, gap, 0))
Ok(stack(flat, align, gap, 0, alternator))
}

/// Layout the inner contents of a matrix.
Expand Down Expand Up @@ -480,7 +484,7 @@ fn layout_mat_body(
let mut y = Abs::zero();

for (cell, &(ascent, descent)) in col.into_iter().zip(&heights) {
let cell = cell.into_line_frame(&points, FixedAlignment::Center);
let cell = cell.into_line_frame(&points, LeftRightAlternator::Right);
let pos = Point::new(
if points.is_empty() { x + (rcol - cell.width()) / 2.0 } else { x },
y + ascent - cell.ascent(),
Expand Down
54 changes: 26 additions & 28 deletions crates/typst/src/math/row.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::iter::once;
use unicode_math_class::MathClass;

use crate::foundations::{Resolve, StyleChain};
use crate::layout::{Abs, AlignElem, Em, FixedAlignment, Frame, FrameKind, Point, Size};
use crate::layout::{Abs, AlignElem, Em, Frame, FrameKind, Point, Size};
use crate::math::{
alignments, scaled_font_size, spacing, EquationElem, FrameFragment, MathContext,
MathFragment, MathParItem, MathSize,
Expand Down Expand Up @@ -140,7 +140,7 @@ impl MathRun {

pub fn into_frame(self, ctx: &MathContext, styles: StyleChain) -> Frame {
if !self.is_multiline() {
self.into_line_frame(&[], AlignElem::alignment_in(styles).resolve(styles).x)
self.into_line_frame(&[], LeftRightAlternator::Right)
} else {
self.multiline_frame_builder(ctx, styles).build()
}
Expand Down Expand Up @@ -181,7 +181,7 @@ impl MathRun {
continue;
}

let sub = row.into_line_frame(&alignments.points, align);
let sub = row.into_line_frame(&alignments.points, LeftRightAlternator::Right);
if i > 0 {
size.y += leading;
}
Expand All @@ -200,43 +200,37 @@ impl MathRun {

/// Lay out [`MathFragment`]s into a one-row [`Frame`], using the
/// caller-provided alignment points.
pub fn into_line_frame(self, points: &[Abs], align: FixedAlignment) -> Frame {
pub fn into_line_frame(
self,
points: &[Abs],
mut alternator: LeftRightAlternator,
) -> Frame {
let ascent = self.ascent();
let mut frame = Frame::soft(Size::new(Abs::zero(), ascent + self.descent()));
frame.set_baseline(ascent);

let mut next_x = {
let mut widths = Vec::new();
if !points.is_empty() && align != FixedAlignment::Start {
let mut width = Abs::zero();
for fragment in self.iter() {
if matches!(fragment, MathFragment::Align) {
widths.push(width);
width = Abs::zero();
} else {
width += fragment.width();
}
}
widths.push(width);
}
let widths = widths;
let widths: Vec<Abs> = if points.is_empty() {
vec![]
} else {
self.iter()
.as_slice()
.split(|e| matches!(e, MathFragment::Align))
.map(|chunk| chunk.iter().map(|e| e.width()).sum())
.collect()
};

let mut prev_points = once(Abs::zero()).chain(points.iter().copied());
let mut point_widths = points.iter().copied().zip(widths);
let mut alternator = LeftRightAlternator::Right;
move || match align {
FixedAlignment::Start => prev_points.next(),
FixedAlignment::End => {
point_widths.next().map(|(point, width)| point - width)
}
_ => point_widths
move || {
point_widths
.next()
.zip(prev_points.next())
.zip(alternator.next())
.map(|(((point, width), prev_point), alternator)| match alternator {
LeftRightAlternator::Left => prev_point,
LeftRightAlternator::Right => point - width,
}),
_ => prev_point,
})
}
};
let mut x = next_x().unwrap_or_default();
Expand Down Expand Up @@ -352,8 +346,11 @@ impl<T: Into<MathFragment>> From<T> for MathRun {
}
}

/// An iterator that alternates between the `Left` and `Right` values, if the
/// initial value is not `None`.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
enum LeftRightAlternator {
pub enum LeftRightAlternator {
None,
Left,
Right,
}
Expand All @@ -364,6 +361,7 @@ impl Iterator for LeftRightAlternator {
fn next(&mut self) -> Option<Self::Item> {
let r = Some(*self);
match self {
Self::None => {}
Self::Left => *self = Self::Right,
Self::Right => *self = Self::Left,
}
Expand Down
16 changes: 10 additions & 6 deletions crates/typst/src/math/underover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ use crate::foundations::{elem, Content, Packed, StyleChain};
use crate::layout::{Abs, Em, FixedAlignment, Frame, FrameItem, Point, Size};
use crate::math::{
alignments, scaled_font_size, style_cramped, style_for_subscript, AlignmentResult,
FrameFragment, GlyphFragment, LayoutMath, MathContext, MathRun, Scaled,
FrameFragment, GlyphFragment, LayoutMath, LeftRightAlternator, MathContext, MathRun,
Scaled,
};
use crate::syntax::Span;
use crate::text::TextElem;
Expand Down Expand Up @@ -290,36 +291,39 @@ fn layout_underoverspreader(
baseline = rows.len() - 1;
}

let frame = stack(rows, FixedAlignment::Center, gap, baseline);
let frame =
stack(rows, FixedAlignment::Center, gap, baseline, LeftRightAlternator::Right);
ctx.push(FrameFragment::new(ctx, styles, frame).with_class(body_class));

Ok(())
}

/// Stack rows on top of each other.
///
/// Add a `gap` between each row and uses the baseline of the `baseline`th
/// row for the whole frame.
/// Add a `gap` between each row and uses the baseline of the `baseline`-th
/// row for the whole frame. `alternator` controls the left/right alternating
/// alignment behavior of `AlignPointElem` in the rows.
pub(super) fn stack(
rows: Vec<MathRun>,
align: FixedAlignment,
gap: Abs,
baseline: usize,
alternator: LeftRightAlternator,
) -> Frame {
let rows: Vec<_> = rows.into_iter().flat_map(|r| r.rows()).collect();
let AlignmentResult { points, width } = alignments(&rows);
let rows: Vec<_> = rows
.into_iter()
.map(|row| row.into_line_frame(&points, align))
.map(|row| row.into_line_frame(&points, alternator))
.collect();

let mut y = Abs::zero();
let mut frame = Frame::soft(Size::new(
width,
rows.iter().map(|row| row.height()).sum::<Abs>()
+ rows.len().saturating_sub(1) as f64 * gap,
));

let mut y = Abs::zero();
for (i, row) in rows.into_iter().enumerate() {
let x = align.position(width - row.width());
let pos = Point::new(x, y);
Expand Down
Binary file added tests/ref/issue-3973-math-equation-align.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions tests/suite/math/alignment.typ
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,20 @@ $
a &=b & quad c&=d \
e &=f & g&=h
$

--- issue-3973-math-equation-align ---
// In this bug, the alignment set with "show math.equation: set align(...)"
// overrides the left-right alternating behavior of alignment points.
#let equations = [
$ a + b &= c \
e &= f + g + h $
$ a &= b + c \
e + f + g &= h $
]
#equations

#show math.equation: set align(start)
#equations

#show math.equation: set align(end)
#equations