mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Make block equations breakable (#4226)
Co-authored-by: Laurenz <laurmaedje@gmail.com>
This commit is contained in:
parent
b75f0a82d4
commit
5f6d942519
@ -10,8 +10,8 @@ use crate::foundations::{
|
||||
};
|
||||
use crate::introspection::{Count, Counter, CounterUpdate, Locatable};
|
||||
use crate::layout::{
|
||||
Abs, AlignElem, Alignment, Axes, Em, FixedAlignment, Frame, LayoutMultiple,
|
||||
LayoutSingle, OuterHAlignment, Point, Regions, Size, SpecificAlignment, VAlignment,
|
||||
Abs, AlignElem, Alignment, Axes, BlockElem, Em, FixedAlignment, Fragment, Frame,
|
||||
LayoutMultiple, OuterHAlignment, Point, Regions, Size, SpecificAlignment, VAlignment,
|
||||
};
|
||||
use crate::math::{
|
||||
scaled_font_size, LayoutMath, MathContext, MathRunFrameBuilder, MathSize, MathVariant,
|
||||
@ -51,7 +51,7 @@ use crate::World;
|
||||
Locatable,
|
||||
Synthesize,
|
||||
ShowSet,
|
||||
LayoutSingle,
|
||||
LayoutMultiple,
|
||||
LayoutMath,
|
||||
Count,
|
||||
LocalName,
|
||||
@ -174,6 +174,7 @@ impl ShowSet for Packed<EquationElem> {
|
||||
let mut out = Styles::new();
|
||||
if self.block(styles) {
|
||||
out.set(AlignElem::set_alignment(Alignment::CENTER));
|
||||
out.set(BlockElem::set_breakable(false));
|
||||
out.set(EquationElem::set_size(MathSize::Display));
|
||||
} else {
|
||||
out.set(EquationElem::set_size(MathSize::Text));
|
||||
@ -248,26 +249,89 @@ impl Packed<EquationElem> {
|
||||
}
|
||||
}
|
||||
|
||||
impl LayoutSingle for Packed<EquationElem> {
|
||||
impl LayoutMultiple for Packed<EquationElem> {
|
||||
#[typst_macros::time(name = "math.equation", span = self.span())]
|
||||
fn layout(
|
||||
&self,
|
||||
engine: &mut Engine,
|
||||
styles: StyleChain,
|
||||
regions: Regions,
|
||||
) -> SourceResult<Frame> {
|
||||
) -> SourceResult<Fragment> {
|
||||
assert!(self.block(styles));
|
||||
|
||||
let span = self.span();
|
||||
let font = find_math_font(engine, styles, span)?;
|
||||
|
||||
let mut ctx = MathContext::new(engine, styles, regions, &font);
|
||||
let equation_builder = ctx
|
||||
let full_equation_builder = ctx
|
||||
.layout_into_run(self, styles)?
|
||||
.multiline_frame_builder(&ctx, styles);
|
||||
let width = full_equation_builder.size.x;
|
||||
|
||||
let equation_builders = if BlockElem::breakable_in(styles) {
|
||||
let mut rows = full_equation_builder.frames.into_iter().peekable();
|
||||
let mut equation_builders = vec![];
|
||||
let mut last_first_pos = Point::zero();
|
||||
|
||||
for region in regions.iter() {
|
||||
// Keep track of the position of the first row in this region,
|
||||
// so that the offset can be reverted later.
|
||||
let Some(&(_, first_pos)) = rows.peek() else { break };
|
||||
last_first_pos = first_pos;
|
||||
|
||||
let mut frames = vec![];
|
||||
let mut height = Abs::zero();
|
||||
while let Some((sub, pos)) = rows.peek() {
|
||||
let mut pos = *pos;
|
||||
pos.y -= first_pos.y;
|
||||
|
||||
// Finish this region if the line doesn't fit. Only do it if
|
||||
// we placed at least one line _or_ we still have non-last
|
||||
// regions. Crucially, we don't want to infinitely create
|
||||
// new regions which are too small.
|
||||
if !region.y.fits(sub.height() + pos.y)
|
||||
&& (!frames.is_empty() || !regions.in_last())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
let (sub, _) = rows.next().unwrap();
|
||||
height = height.max(pos.y + sub.height());
|
||||
frames.push((sub, pos));
|
||||
}
|
||||
|
||||
equation_builders
|
||||
.push(MathRunFrameBuilder { frames, size: Size::new(width, height) });
|
||||
}
|
||||
|
||||
// Append remaining rows to the equation builder of the last region.
|
||||
if let Some(equation_builder) = equation_builders.last_mut() {
|
||||
equation_builder.frames.extend(rows.map(|(frame, mut pos)| {
|
||||
pos.y -= last_first_pos.y;
|
||||
(frame, pos)
|
||||
}));
|
||||
|
||||
let height = equation_builder
|
||||
.frames
|
||||
.iter()
|
||||
.map(|(frame, pos)| frame.height() + pos.y)
|
||||
.max()
|
||||
.unwrap_or(equation_builder.size.y);
|
||||
|
||||
equation_builder.size.y = height;
|
||||
}
|
||||
|
||||
equation_builders
|
||||
} else {
|
||||
vec![full_equation_builder]
|
||||
};
|
||||
|
||||
let Some(numbering) = (**self).numbering(styles) else {
|
||||
return Ok(equation_builder.build());
|
||||
let frames = equation_builders
|
||||
.into_iter()
|
||||
.map(MathRunFrameBuilder::build)
|
||||
.collect();
|
||||
return Ok(Fragment::frames(frames));
|
||||
};
|
||||
|
||||
let pod = Regions::one(regions.base(), Axes::splat(false));
|
||||
@ -286,16 +350,22 @@ impl LayoutSingle for Packed<EquationElem> {
|
||||
SpecificAlignment::Both(h, v) => SpecificAlignment::Both(h, v),
|
||||
};
|
||||
|
||||
let frame = add_equation_number(
|
||||
equation_builder,
|
||||
number,
|
||||
number_align.resolve(styles),
|
||||
AlignElem::alignment_in(styles).resolve(styles).x,
|
||||
regions.size.x,
|
||||
full_number_width,
|
||||
);
|
||||
// Add equation numbers to each equation region.
|
||||
let frames = equation_builders
|
||||
.into_iter()
|
||||
.map(|builder| {
|
||||
add_equation_number(
|
||||
builder,
|
||||
number.clone(),
|
||||
number_align.resolve(styles),
|
||||
AlignElem::alignment_in(styles).resolve(styles).x,
|
||||
regions.size.x,
|
||||
full_number_width,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(frame)
|
||||
Ok(Fragment::frames(frames))
|
||||
}
|
||||
}
|
||||
|
||||
|
BIN
tests/ref/math-pagebreaking-numbered.png
Normal file
BIN
tests/ref/math-pagebreaking-numbered.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 735 B |
BIN
tests/ref/math-pagebreaking.png
Normal file
BIN
tests/ref/math-pagebreaking.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 705 B |
@ -107,6 +107,29 @@ Multiple trailing line breaks.
|
||||
$ $\
|
||||
Nothing: $ $, just empty.
|
||||
|
||||
--- math-pagebreaking ---
|
||||
// Test breaking of equations at page boundaries.
|
||||
#set page(height: 5em)
|
||||
#show math.equation: set block(breakable: true)
|
||||
|
||||
$ a &+ b + & c \
|
||||
a &+ b & && + d \
|
||||
a &+ b + & c && + d \
|
||||
& & c && + d \
|
||||
&= 0 $
|
||||
|
||||
--- math-pagebreaking-numbered ---
|
||||
// Test breaking of equations with numbering.
|
||||
#set page(height: 5em)
|
||||
#set math.equation(numbering: "1")
|
||||
#show math.equation: set block(breakable: true)
|
||||
|
||||
$ a &+ b + & c \
|
||||
a &+ b & && + d \
|
||||
a &+ b + & c && + d \
|
||||
& & c && + d \
|
||||
&= 0 $
|
||||
|
||||
--- issue-1948-math-text-break ---
|
||||
// Test text with linebreaks in math.
|
||||
$ x := "a\nb\nc\nd\ne" $
|
||||
|
Loading…
x
Reference in New Issue
Block a user