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::introspection::{Count, Counter, CounterUpdate, Locatable};
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
Abs, AlignElem, Alignment, Axes, Em, FixedAlignment, Frame, LayoutMultiple,
|
Abs, AlignElem, Alignment, Axes, BlockElem, Em, FixedAlignment, Fragment, Frame,
|
||||||
LayoutSingle, OuterHAlignment, Point, Regions, Size, SpecificAlignment, VAlignment,
|
LayoutMultiple, OuterHAlignment, Point, Regions, Size, SpecificAlignment, VAlignment,
|
||||||
};
|
};
|
||||||
use crate::math::{
|
use crate::math::{
|
||||||
scaled_font_size, LayoutMath, MathContext, MathRunFrameBuilder, MathSize, MathVariant,
|
scaled_font_size, LayoutMath, MathContext, MathRunFrameBuilder, MathSize, MathVariant,
|
||||||
@ -51,7 +51,7 @@ use crate::World;
|
|||||||
Locatable,
|
Locatable,
|
||||||
Synthesize,
|
Synthesize,
|
||||||
ShowSet,
|
ShowSet,
|
||||||
LayoutSingle,
|
LayoutMultiple,
|
||||||
LayoutMath,
|
LayoutMath,
|
||||||
Count,
|
Count,
|
||||||
LocalName,
|
LocalName,
|
||||||
@ -174,6 +174,7 @@ impl ShowSet for Packed<EquationElem> {
|
|||||||
let mut out = Styles::new();
|
let mut out = Styles::new();
|
||||||
if self.block(styles) {
|
if self.block(styles) {
|
||||||
out.set(AlignElem::set_alignment(Alignment::CENTER));
|
out.set(AlignElem::set_alignment(Alignment::CENTER));
|
||||||
|
out.set(BlockElem::set_breakable(false));
|
||||||
out.set(EquationElem::set_size(MathSize::Display));
|
out.set(EquationElem::set_size(MathSize::Display));
|
||||||
} else {
|
} else {
|
||||||
out.set(EquationElem::set_size(MathSize::Text));
|
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())]
|
#[typst_macros::time(name = "math.equation", span = self.span())]
|
||||||
fn layout(
|
fn layout(
|
||||||
&self,
|
&self,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: Regions,
|
regions: Regions,
|
||||||
) -> SourceResult<Frame> {
|
) -> SourceResult<Fragment> {
|
||||||
assert!(self.block(styles));
|
assert!(self.block(styles));
|
||||||
|
|
||||||
let span = self.span();
|
let span = self.span();
|
||||||
let font = find_math_font(engine, styles, span)?;
|
let font = find_math_font(engine, styles, span)?;
|
||||||
|
|
||||||
let mut ctx = MathContext::new(engine, styles, regions, &font);
|
let mut ctx = MathContext::new(engine, styles, regions, &font);
|
||||||
let equation_builder = ctx
|
let full_equation_builder = ctx
|
||||||
.layout_into_run(self, styles)?
|
.layout_into_run(self, styles)?
|
||||||
.multiline_frame_builder(&ctx, 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 {
|
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));
|
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),
|
SpecificAlignment::Both(h, v) => SpecificAlignment::Both(h, v),
|
||||||
};
|
};
|
||||||
|
|
||||||
let frame = add_equation_number(
|
// Add equation numbers to each equation region.
|
||||||
equation_builder,
|
let frames = equation_builders
|
||||||
number,
|
.into_iter()
|
||||||
number_align.resolve(styles),
|
.map(|builder| {
|
||||||
AlignElem::alignment_in(styles).resolve(styles).x,
|
add_equation_number(
|
||||||
regions.size.x,
|
builder,
|
||||||
full_number_width,
|
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.
|
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 ---
|
--- issue-1948-math-text-break ---
|
||||||
// Test text with linebreaks in math.
|
// Test text with linebreaks in math.
|
||||||
$ x := "a\nb\nc\nd\ne" $
|
$ x := "a\nb\nc\nd\ne" $
|
||||||
|
Loading…
x
Reference in New Issue
Block a user