1use typst_library::diag::{bail, SourceResult};
2use typst_library::engine::Engine;
3use typst_library::foundations::{Packed, Resolve, StyleChain};
4use typst_library::introspection::Locator;
5use typst_library::layout::{
6 Abs, AlignElem, Axes, Frame, Point, Region, RepeatElem, Size,
7};
8use typst_utils::Numeric;
9
10#[typst_macros::time(span = elem.span())]
12pub fn layout_repeat(
13 elem: &Packed<RepeatElem>,
14 engine: &mut Engine,
15 locator: Locator,
16 styles: StyleChain,
17 region: Region,
18) -> SourceResult<Frame> {
19 let pod = Region::new(region.size, Axes::new(false, false));
20 let piece = crate::layout_frame(engine, &elem.body, locator, styles, pod)?;
21 let size = Size::new(region.size.x, piece.height());
22
23 if !size.is_finite() {
24 bail!(elem.span(), "repeat with no size restrictions");
25 }
26
27 let mut frame = Frame::soft(size);
28 if piece.has_baseline() {
29 frame.set_baseline(piece.baseline());
30 }
31
32 let mut gap = elem.gap(styles).resolve(styles);
33 let fill = region.size.x;
34 let width = piece.width();
35
36 let count = ((fill + gap) / (width + gap)).floor();
48 let remaining = (fill + gap) % (width + gap);
49
50 let justify = elem.justify(styles);
51 if justify {
52 gap += remaining / (count - 1.0);
53 }
54
55 let align = AlignElem::alignment_in(styles).resolve(styles);
56 let mut offset = Abs::zero();
57 if count == 1.0 || !justify {
58 offset += align.x.position(remaining);
59 }
60
61 if width > Abs::zero() {
62 for _ in 0..(count as usize).min(1000) {
63 frame.push_frame(Point::with_x(offset), piece.clone());
64 offset += width + gap;
65 }
66 }
67
68 Ok(frame)
69}