mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +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::{
|
use crate::math::{
|
||||||
alignments, scaled_font_size, stack, style_for_denominator, AlignmentResult,
|
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::syntax::{Span, Spanned};
|
||||||
use crate::text::TextElem;
|
use crate::text::TextElem;
|
||||||
@ -67,6 +68,7 @@ impl LayoutMath for Packed<VecElem> {
|
|||||||
self.children(),
|
self.children(),
|
||||||
FixedAlignment::Center,
|
FixedAlignment::Center,
|
||||||
self.gap(styles),
|
self.gap(styles),
|
||||||
|
LeftRightAlternator::Right,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
layout_delimiters(
|
layout_delimiters(
|
||||||
@ -324,6 +326,7 @@ impl LayoutMath for Packed<CasesElem> {
|
|||||||
self.children(),
|
self.children(),
|
||||||
FixedAlignment::Start,
|
FixedAlignment::Start,
|
||||||
self.gap(styles),
|
self.gap(styles),
|
||||||
|
LeftRightAlternator::None,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let (open, close) = if self.reverse(styles) {
|
let (open, close) = if self.reverse(styles) {
|
||||||
@ -387,6 +390,7 @@ fn layout_vec_body(
|
|||||||
column: &[Content],
|
column: &[Content],
|
||||||
align: FixedAlignment,
|
align: FixedAlignment,
|
||||||
row_gap: Rel<Abs>,
|
row_gap: Rel<Abs>,
|
||||||
|
alternator: LeftRightAlternator,
|
||||||
) -> SourceResult<Frame> {
|
) -> SourceResult<Frame> {
|
||||||
let gap = row_gap.relative_to(ctx.regions.base().y);
|
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))?);
|
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.
|
/// Layout the inner contents of a matrix.
|
||||||
@ -480,7 +484,7 @@ fn layout_mat_body(
|
|||||||
let mut y = Abs::zero();
|
let mut y = Abs::zero();
|
||||||
|
|
||||||
for (cell, &(ascent, descent)) in col.into_iter().zip(&heights) {
|
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(
|
let pos = Point::new(
|
||||||
if points.is_empty() { x + (rcol - cell.width()) / 2.0 } else { x },
|
if points.is_empty() { x + (rcol - cell.width()) / 2.0 } else { x },
|
||||||
y + ascent - cell.ascent(),
|
y + ascent - cell.ascent(),
|
||||||
|
@ -3,7 +3,7 @@ use std::iter::once;
|
|||||||
use unicode_math_class::MathClass;
|
use unicode_math_class::MathClass;
|
||||||
|
|
||||||
use crate::foundations::{Resolve, StyleChain};
|
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::{
|
use crate::math::{
|
||||||
alignments, scaled_font_size, spacing, EquationElem, FrameFragment, MathContext,
|
alignments, scaled_font_size, spacing, EquationElem, FrameFragment, MathContext,
|
||||||
MathFragment, MathParItem, MathSize,
|
MathFragment, MathParItem, MathSize,
|
||||||
@ -140,7 +140,7 @@ impl MathRun {
|
|||||||
|
|
||||||
pub fn into_frame(self, ctx: &MathContext, styles: StyleChain) -> Frame {
|
pub fn into_frame(self, ctx: &MathContext, styles: StyleChain) -> Frame {
|
||||||
if !self.is_multiline() {
|
if !self.is_multiline() {
|
||||||
self.into_line_frame(&[], AlignElem::alignment_in(styles).resolve(styles).x)
|
self.into_line_frame(&[], LeftRightAlternator::Right)
|
||||||
} else {
|
} else {
|
||||||
self.multiline_frame_builder(ctx, styles).build()
|
self.multiline_frame_builder(ctx, styles).build()
|
||||||
}
|
}
|
||||||
@ -181,7 +181,7 @@ impl MathRun {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let sub = row.into_line_frame(&alignments.points, align);
|
let sub = row.into_line_frame(&alignments.points, LeftRightAlternator::Right);
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
size.y += leading;
|
size.y += leading;
|
||||||
}
|
}
|
||||||
@ -200,43 +200,37 @@ impl MathRun {
|
|||||||
|
|
||||||
/// Lay out [`MathFragment`]s into a one-row [`Frame`], using the
|
/// Lay out [`MathFragment`]s into a one-row [`Frame`], using the
|
||||||
/// caller-provided alignment points.
|
/// 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 ascent = self.ascent();
|
||||||
let mut frame = Frame::soft(Size::new(Abs::zero(), ascent + self.descent()));
|
let mut frame = Frame::soft(Size::new(Abs::zero(), ascent + self.descent()));
|
||||||
frame.set_baseline(ascent);
|
frame.set_baseline(ascent);
|
||||||
|
|
||||||
let mut next_x = {
|
let mut next_x = {
|
||||||
let mut widths = Vec::new();
|
let widths: Vec<Abs> = if points.is_empty() {
|
||||||
if !points.is_empty() && align != FixedAlignment::Start {
|
vec![]
|
||||||
let mut width = Abs::zero();
|
} else {
|
||||||
for fragment in self.iter() {
|
self.iter()
|
||||||
if matches!(fragment, MathFragment::Align) {
|
.as_slice()
|
||||||
widths.push(width);
|
.split(|e| matches!(e, MathFragment::Align))
|
||||||
width = Abs::zero();
|
.map(|chunk| chunk.iter().map(|e| e.width()).sum())
|
||||||
} else {
|
.collect()
|
||||||
width += fragment.width();
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
widths.push(width);
|
|
||||||
}
|
|
||||||
let widths = widths;
|
|
||||||
|
|
||||||
let mut prev_points = once(Abs::zero()).chain(points.iter().copied());
|
let mut prev_points = once(Abs::zero()).chain(points.iter().copied());
|
||||||
let mut point_widths = points.iter().copied().zip(widths);
|
let mut point_widths = points.iter().copied().zip(widths);
|
||||||
let mut alternator = LeftRightAlternator::Right;
|
move || {
|
||||||
move || match align {
|
point_widths
|
||||||
FixedAlignment::Start => prev_points.next(),
|
|
||||||
FixedAlignment::End => {
|
|
||||||
point_widths.next().map(|(point, width)| point - width)
|
|
||||||
}
|
|
||||||
_ => point_widths
|
|
||||||
.next()
|
.next()
|
||||||
.zip(prev_points.next())
|
.zip(prev_points.next())
|
||||||
.zip(alternator.next())
|
.zip(alternator.next())
|
||||||
.map(|(((point, width), prev_point), alternator)| match alternator {
|
.map(|(((point, width), prev_point), alternator)| match alternator {
|
||||||
LeftRightAlternator::Left => prev_point,
|
|
||||||
LeftRightAlternator::Right => point - width,
|
LeftRightAlternator::Right => point - width,
|
||||||
}),
|
_ => prev_point,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let mut x = next_x().unwrap_or_default();
|
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)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
enum LeftRightAlternator {
|
pub enum LeftRightAlternator {
|
||||||
|
None,
|
||||||
Left,
|
Left,
|
||||||
Right,
|
Right,
|
||||||
}
|
}
|
||||||
@ -364,6 +361,7 @@ impl Iterator for LeftRightAlternator {
|
|||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
let r = Some(*self);
|
let r = Some(*self);
|
||||||
match self {
|
match self {
|
||||||
|
Self::None => {}
|
||||||
Self::Left => *self = Self::Right,
|
Self::Left => *self = Self::Right,
|
||||||
Self::Right => *self = Self::Left,
|
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::layout::{Abs, Em, FixedAlignment, Frame, FrameItem, Point, Size};
|
||||||
use crate::math::{
|
use crate::math::{
|
||||||
alignments, scaled_font_size, style_cramped, style_for_subscript, AlignmentResult,
|
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::syntax::Span;
|
||||||
use crate::text::TextElem;
|
use crate::text::TextElem;
|
||||||
@ -290,7 +291,8 @@ fn layout_underoverspreader(
|
|||||||
baseline = rows.len() - 1;
|
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));
|
ctx.push(FrameFragment::new(ctx, styles, frame).with_class(body_class));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -298,28 +300,30 @@ fn layout_underoverspreader(
|
|||||||
|
|
||||||
/// Stack rows on top of each other.
|
/// Stack rows on top of each other.
|
||||||
///
|
///
|
||||||
/// Add a `gap` between each row and uses the baseline of the `baseline`th
|
/// Add a `gap` between each row and uses the baseline of the `baseline`-th
|
||||||
/// row for the whole frame.
|
/// row for the whole frame. `alternator` controls the left/right alternating
|
||||||
|
/// alignment behavior of `AlignPointElem` in the rows.
|
||||||
pub(super) fn stack(
|
pub(super) fn stack(
|
||||||
rows: Vec<MathRun>,
|
rows: Vec<MathRun>,
|
||||||
align: FixedAlignment,
|
align: FixedAlignment,
|
||||||
gap: Abs,
|
gap: Abs,
|
||||||
baseline: usize,
|
baseline: usize,
|
||||||
|
alternator: LeftRightAlternator,
|
||||||
) -> Frame {
|
) -> Frame {
|
||||||
let rows: Vec<_> = rows.into_iter().flat_map(|r| r.rows()).collect();
|
let rows: Vec<_> = rows.into_iter().flat_map(|r| r.rows()).collect();
|
||||||
let AlignmentResult { points, width } = alignments(&rows);
|
let AlignmentResult { points, width } = alignments(&rows);
|
||||||
let rows: Vec<_> = rows
|
let rows: Vec<_> = rows
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|row| row.into_line_frame(&points, align))
|
.map(|row| row.into_line_frame(&points, alternator))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut y = Abs::zero();
|
|
||||||
let mut frame = Frame::soft(Size::new(
|
let mut frame = Frame::soft(Size::new(
|
||||||
width,
|
width,
|
||||||
rows.iter().map(|row| row.height()).sum::<Abs>()
|
rows.iter().map(|row| row.height()).sum::<Abs>()
|
||||||
+ rows.len().saturating_sub(1) as f64 * gap,
|
+ rows.len().saturating_sub(1) as f64 * gap,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
let mut y = Abs::zero();
|
||||||
for (i, row) in rows.into_iter().enumerate() {
|
for (i, row) in rows.into_iter().enumerate() {
|
||||||
let x = align.position(width - row.width());
|
let x = align.position(width - row.width());
|
||||||
let pos = Point::new(x, y);
|
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 \
|
a &=b & quad c&=d \
|
||||||
e &=f & g&=h
|
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