mirror of
https://github.com/typst/typst
synced 2025-05-16 01:55:28 +08:00
70 lines
2.1 KiB
Rust
70 lines
2.1 KiB
Rust
use typst_library::diag::{bail, SourceResult};
|
|
use typst_library::engine::Engine;
|
|
use typst_library::foundations::{Packed, Resolve, StyleChain};
|
|
use typst_library::introspection::Locator;
|
|
use typst_library::layout::{
|
|
Abs, AlignElem, Axes, Frame, Point, Region, RepeatElem, Size,
|
|
};
|
|
use typst_utils::Numeric;
|
|
|
|
/// Layout the repeated content.
|
|
#[typst_macros::time(span = elem.span())]
|
|
pub fn layout_repeat(
|
|
elem: &Packed<RepeatElem>,
|
|
engine: &mut Engine,
|
|
locator: Locator,
|
|
styles: StyleChain,
|
|
region: Region,
|
|
) -> SourceResult<Frame> {
|
|
let pod = Region::new(region.size, Axes::new(false, false));
|
|
let piece = crate::layout_frame(engine, &elem.body, locator, styles, pod)?;
|
|
let size = Size::new(region.size.x, piece.height());
|
|
|
|
if !size.is_finite() {
|
|
bail!(elem.span(), "repeat with no size restrictions");
|
|
}
|
|
|
|
let mut frame = Frame::soft(size);
|
|
if piece.has_baseline() {
|
|
frame.set_baseline(piece.baseline());
|
|
}
|
|
|
|
let mut gap = elem.gap(styles).resolve(styles);
|
|
let fill = region.size.x;
|
|
let width = piece.width();
|
|
|
|
// We need to fit the body N times, but the number of gaps is (N - 1):
|
|
// N * w + (N - 1) * g ≤ F
|
|
// where N - body count (count)
|
|
// w - body width (width)
|
|
// g - gap width (gap)
|
|
// F - available space to fill (fill)
|
|
//
|
|
// N * w + N * g - g ≤ F
|
|
// N * (w + g) ≤ F + g
|
|
// N ≤ (F + g) / (w + g)
|
|
// N = ⌊(F + g) / (w + g)⌋
|
|
let count = ((fill + gap) / (width + gap)).floor();
|
|
let remaining = (fill + gap) % (width + gap);
|
|
|
|
let justify = elem.justify(styles);
|
|
if justify {
|
|
gap += remaining / (count - 1.0);
|
|
}
|
|
|
|
let align = AlignElem::alignment_in(styles).resolve(styles);
|
|
let mut offset = Abs::zero();
|
|
if count == 1.0 || !justify {
|
|
offset += align.x.position(remaining);
|
|
}
|
|
|
|
if width > Abs::zero() {
|
|
for _ in 0..(count as usize).min(1000) {
|
|
frame.push_frame(Point::with_x(offset), piece.clone());
|
|
offset += width + gap;
|
|
}
|
|
}
|
|
|
|
Ok(frame)
|
|
}
|