mirror of
https://github.com/typst/typst
synced 2025-05-13 20:46:23 +08:00
show math.equation: set align(..)
shall not break alignment points (#4094)
This commit is contained in:
parent
2d32ac73b6
commit
484a0e60d8
@ -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;
|
||||
@ -67,6 +68,7 @@ impl LayoutMath for Packed<VecElem> {
|
||||
self.children(),
|
||||
FixedAlignment::Center,
|
||||
self.gap(styles),
|
||||
LeftRightAlternator::Right,
|
||||
)?;
|
||||
|
||||
layout_delimiters(
|
||||
@ -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) {
|
||||
@ -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);
|
||||
|
||||
@ -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.
|
||||
@ -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(),
|
||||
|
@ -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,
|
||||
@ -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()
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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();
|
||||
@ -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,
|
||||
}
|
||||
@ -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,
|
||||
}
|
||||
|
@ -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;
|
||||
@ -290,7 +291,8 @@ 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(())
|
||||
@ -298,28 +300,30 @@ fn layout_underoverspreader(
|
||||
|
||||
/// 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);
|
||||
|
BIN
tests/ref/issue-3973-math-equation-align.png
Normal file
BIN
tests/ref/issue-3973-math-equation-align.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.2 KiB |
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user