summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/typst/src/layout/columns.rs9
-rw-r--r--crates/typst/src/layout/container.rs12
-rw-r--r--crates/typst/src/layout/flow.rs492
-rw-r--r--crates/typst/src/layout/grid/cells.rs5
-rw-r--r--crates/typst/src/layout/grid/layout.rs12
-rw-r--r--crates/typst/src/layout/grid/mod.rs2
-rw-r--r--crates/typst/src/layout/grid/rowspans.rs6
-rw-r--r--crates/typst/src/layout/inline/mod.rs83
-rw-r--r--crates/typst/src/layout/layout.rs4
-rw-r--r--crates/typst/src/layout/measure.rs6
-rw-r--r--crates/typst/src/layout/mod.rs126
-rw-r--r--crates/typst/src/layout/pad.rs5
-rw-r--r--crates/typst/src/layout/page.rs261
-rw-r--r--crates/typst/src/layout/place.rs9
-rw-r--r--crates/typst/src/layout/regions.rs32
-rw-r--r--crates/typst/src/layout/repeat.rs6
-rw-r--r--crates/typst/src/layout/stack.rs7
-rw-r--r--crates/typst/src/layout/transform.rs25
-rw-r--r--crates/typst/src/lib.rs13
-rw-r--r--crates/typst/src/math/ctx.rs48
-rw-r--r--crates/typst/src/math/equation.rs15
-rw-r--r--crates/typst/src/math/matrix.rs4
-rw-r--r--crates/typst/src/model/document.rs56
-rw-r--r--crates/typst/src/model/heading.rs13
-rw-r--r--crates/typst/src/model/par.rs30
-rw-r--r--crates/typst/src/realize/mod.rs44
-rw-r--r--crates/typst/src/visualize/pattern.rs6
-rw-r--r--crates/typst/src/visualize/shape.rs12
28 files changed, 639 insertions, 704 deletions
diff --git a/crates/typst/src/layout/columns.rs b/crates/typst/src/layout/columns.rs
index 503cb857..7018a8ed 100644
--- a/crates/typst/src/layout/columns.rs
+++ b/crates/typst/src/layout/columns.rs
@@ -5,7 +5,8 @@ use crate::engine::Engine;
use crate::foundations::{elem, Content, NativeElement, Packed, Show, StyleChain};
use crate::introspection::Locator;
use crate::layout::{
- Abs, Axes, BlockElem, Dir, Fragment, Frame, Length, Point, Ratio, Regions, Rel, Size,
+ layout_fragment, Abs, Axes, BlockElem, Dir, Fragment, Frame, Length, Point, Ratio,
+ Regions, Rel, Size,
};
use crate::realize::{Behave, Behaviour};
use crate::text::TextElem;
@@ -77,12 +78,12 @@ fn layout_columns(
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
- let body = elem.body();
+ let body = &elem.body;
// Separating the infinite space into infinite columns does not make
// much sense.
if !regions.size.x.is_finite() {
- return body.layout(engine, locator, styles, regions);
+ return layout_fragment(engine, body, locator, styles, regions);
}
// Determine the width of the gutter and each column.
@@ -107,7 +108,7 @@ fn layout_columns(
};
// Layout the children.
- let mut frames = body.layout(engine, locator, styles, pod)?.into_iter();
+ let mut frames = layout_fragment(engine, body, locator, styles, pod)?.into_iter();
let mut finished = vec![];
let dir = TextElem::dir_in(styles);
diff --git a/crates/typst/src/layout/container.rs b/crates/typst/src/layout/container.rs
index 58a8d107..83523861 100644
--- a/crates/typst/src/layout/container.rs
+++ b/crates/typst/src/layout/container.rs
@@ -9,8 +9,8 @@ use crate::foundations::{
};
use crate::introspection::Locator;
use crate::layout::{
- Abs, Axes, Corners, Em, Fr, Fragment, Frame, FrameKind, Length, Region, Regions, Rel,
- Sides, Size, Spacing,
+ layout_fragment, layout_frame, Abs, Axes, Corners, Em, Fr, Fragment, Frame,
+ FrameKind, Length, Region, Regions, Rel, Sides, Size, Spacing,
};
use crate::utils::Numeric;
use crate::visualize::{clip_rect, Paint, Stroke};
@@ -141,9 +141,7 @@ impl Packed<BoxElem> {
// If we have a child, layout it into the body. Boxes are boundaries
// for gradient relativeness, so we set the `FrameKind` to `Hard`.
- Some(body) => body
- .layout(engine, locator, styles, pod.into_regions())?
- .into_frame()
+ Some(body) => layout_frame(engine, body, locator, styles, pod)?
.with_kind(FrameKind::Hard),
};
@@ -531,7 +529,7 @@ impl Packed<BlockElem> {
// If we have content as our body, just layout it.
Some(BlockChild::Content(body)) => {
let mut fragment =
- body.layout(engine, locator.relayout(), styles, pod)?;
+ layout_fragment(engine, body, locator.relayout(), styles, pod)?;
// If the body is automatically sized and produced more than one
// fragment, ensure that the width was consistent across all
@@ -552,7 +550,7 @@ impl Packed<BlockElem> {
expand: Axes::new(true, pod.expand.y),
..pod
};
- fragment = body.layout(engine, locator, styles, pod)?;
+ fragment = layout_fragment(engine, body, locator, styles, pod)?;
}
fragment
diff --git a/crates/typst/src/layout/flow.rs b/crates/typst/src/layout/flow.rs
index e15159e1..080fc372 100644
--- a/crates/typst/src/layout/flow.rs
+++ b/crates/typst/src/layout/flow.rs
@@ -1,31 +1,423 @@
-//! Layout flows.
-//!
-//! A *flow* is a collection of block-level layoutable elements.
-//! This is analogous to a paragraph, which is a collection of
-//! inline-level layoutable elements.
+//! Layout of content
+//! - at the top-level, into a [`Document`].
+//! - inside of a container, into a [`Frame`] or [`Fragment`].
use std::fmt::{self, Debug, Formatter};
+use std::ptr;
+
+use comemo::{Track, Tracked, TrackedMut};
use crate::diag::{bail, SourceResult};
-use crate::engine::Engine;
+use crate::engine::{Engine, Route, Sink, Traced};
use crate::foundations::{
elem, Args, Construct, Content, NativeElement, Packed, Resolve, Smart, StyleChain,
};
-use crate::introspection::{Locator, SplitLocator, Tag, TagElem};
+use crate::introspection::{
+ Counter, CounterDisplayElem, CounterKey, Introspector, Locator, LocatorLink,
+ ManualPageCounter, SplitLocator, Tag, TagElem,
+};
use crate::layout::{
- Abs, AlignElem, Axes, BlockElem, ColbreakElem, FixedAlignment, FlushElem, Fr,
- Fragment, Frame, FrameItem, PlaceElem, Point, Ratio, Regions, Rel, Size, Spacing,
- VElem,
+ Abs, AlignElem, Alignment, Axes, Binding, BlockElem, ColbreakElem, ColumnsElem, Dir,
+ FixedAlignment, FlushElem, Fr, Fragment, Frame, FrameItem, HAlignment, Length,
+ OuterVAlignment, Page, PageElem, Paper, Parity, PlaceElem, Point, Ratio, Region,
+ Regions, Rel, Sides, Size, Spacing, VAlignment, VElem,
};
+use crate::model::{Document, Numbering};
use crate::model::{FootnoteElem, FootnoteEntry, ParElem};
use crate::realize::StyleVec;
+use crate::realize::{realize_flow, realize_root, Arenas};
use crate::text::TextElem;
use crate::utils::Numeric;
+use crate::World;
+
+/// Layout content into a document.
+///
+/// This first performs root-level realization and then lays out the resulting
+/// elements. In contrast to [`layout_fragment`], this does not take regions
+/// since the regions are defined by the page configuration in the content and
+/// style chain.
+#[typst_macros::time(name = "document")]
+pub fn layout_document(
+ engine: &mut Engine,
+ content: &Content,
+ styles: StyleChain,
+) -> SourceResult<Document> {
+ layout_document_impl(
+ engine.world,
+ engine.introspector,
+ engine.traced,
+ TrackedMut::reborrow_mut(&mut engine.sink),
+ engine.route.track(),
+ content,
+ styles,
+ )
+}
+
+/// The internal implementation of `layout_document`.
+#[comemo::memoize]
+fn layout_document_impl(
+ world: Tracked<dyn World + '_>,
+ introspector: Tracked<Introspector>,
+ traced: Tracked<Traced>,
+ sink: TrackedMut<Sink>,
+ route: Tracked<Route>,
+ content: &Content,
+ styles: StyleChain,
+) -> SourceResult<Document> {
+ let mut locator = Locator::root().split();
+ let mut engine = Engine {
+ world,
+ introspector,
+ traced,
+ sink,
+ route: Route::extend(route).unnested(),
+ };
+
+ let arenas = Arenas::default();
+ let (children, styles, info) =
+ realize_root(&mut engine, &mut locator, &arenas, content, styles)?;
+
+ let mut peekable = children.chain(&styles).peekable();
+ let iter = std::iter::from_fn(|| {
+ let (child, styles) = peekable.next()?;
+ let extend_to = peekable
+ .peek()
+ .and_then(|(next, _)| *next.to_packed::<PageElem>()?.clear_to()?);
+ let locator = locator.next(&child.span());
+ Some((child, styles, extend_to, locator))
+ });
+
+ let layouts =
+ engine.parallelize(iter, |engine, (child, styles, extend_to, locator)| {
+ if let Some(page) = child.to_packed::<PageElem>() {
+ layout_page_run(engine, page, locator, styles, extend_to)
+ } else {
+ bail!(child.span(), "expected page element");
+ }
+ });
+
+ let mut page_counter = ManualPageCounter::new();
+ let mut pages = Vec::with_capacity(children.len());
+ for result in layouts {
+ let layout = result?;
+ pages.extend(finalize_page_run(&mut engine, layout, &mut page_counter)?);
+ }
+
+ Ok(Document { pages, info, introspector: Introspector::default() })
+}
+
+/// A prepared layout of a page run that can be finalized with access to the
+/// page counter.
+struct PageRunLayout<'a> {
+ page: &'a Packed<PageElem>,
+ locator: SplitLocator<'a>,
+ styles: StyleChain<'a>,
+ extend_to: Option<Parity>,
+ area: Size,
+ margin: Sides<Abs>,
+ two_sided: bool,
+ frames: Vec<Frame>,
+}
+
+/// A document can consist of multiple `PageElem`s, one per run of pages
+/// with equal properties (not one per actual output page!). The `number` is
+/// the physical page number of the first page of this run. It is mutated
+/// while we post-process the pages in this function. This function returns
+/// a fragment consisting of multiple frames, one per output page of this
+/// page run.
+#[typst_macros::time(name = "pages", span = page.span())]
+fn layout_page_run<'a>(
+ engine: &mut Engine,
+ page: &'a Packed<PageElem>,
+ locator: Locator<'a>,
+ styles: StyleChain<'a>,
+ extend_to: Option<Parity>,
+) -> SourceResult<PageRunLayout<'a>> {
+ let mut locator = locator.split();
+
+ // When one of the lengths is infinite the page fits its content along
+ // that axis.
+ let width = page.width(styles).unwrap_or(Abs::inf());
+ let height = page.height(styles).unwrap_or(Abs::inf());
+ let mut size = Size::new(width, height);
+ if page.flipped(styles) {
+ std::mem::swap(&mut size.x, &mut size.y);
+ }
+
+ let mut min = width.min(height);
+ if !min.is_finite() {
+ min = Paper::A4.width();
+ }
+
+ // Determine the margins.
+ let default = Rel::<Length>::from((2.5 / 21.0) * min);
+ let margin = page.margin(styles);
+ let two_sided = margin.two_sided.unwrap_or(false);
+ let margin = margin
+ .sides
+ .map(|side| side.and_then(Smart::custom).unwrap_or(default))
+ .resolve(styles)
+ .relative_to(size);
+
+ // Realize columns.
+ let mut child = page.body().clone();
+ let columns = page.columns(styles);
+ if columns.get() > 1 {
+ child = ColumnsElem::new(child)
+ .with_count(columns)
+ .pack()
+ .spanned(page.span());
+ }
+
+ let area = size - margin.sum_by_axis();
+ let mut regions = Regions::repeat(area, area.map(Abs::is_finite));
+ regions.root = true;
+
+ // Layout the child.
+ let frames =
+ layout_fragment(engine, &child, locator.next(&page.span()), styles, regions)?
+ .into_frames();
+
+ Ok(PageRunLayout {
+ page,
+ locator,
+ styles,
+ extend_to,
+ area,
+ margin,
+ two_sided,
+ frames,
+ })
+}
+
+/// Finalize the layout with access to the next page counter.
+#[typst_macros::time(name = "finalize pages", span = page.span())]
+fn finalize_page_run(
+ engine: &mut Engine,
+ PageRunLayout {
+ page,
+ mut locator,
+ styles,
+ extend_to,
+ area,
+ margin,
+ two_sided,
+ mut frames,
+ }: PageRunLayout<'_>,
+ page_counter: &mut ManualPageCounter,
+) -> SourceResult<Vec<Page>> {
+ // Align the child to the pagebreak's parity.
+ // Check for page count after adding the pending frames
+ if extend_to.is_some_and(|p| !p.matches(page_counter.physical().get() + frames.len()))
+ {
+ // Insert empty page after the current pages.
+ let size = area.map(Abs::is_finite).select(area, Size::zero());
+ frames.push(Frame::hard(size));
+ }
+
+ let fill = page.fill(styles);
+ let foreground = page.foreground(styles);
+ let background = page.background(styles);
+ let header_ascent = page.header_ascent(styles);
+ let footer_descent = page.footer_descent(styles);
+ let numbering = page.numbering(styles);
+ let number_align = page.number_align(styles);
+ let binding =
+ page.binding(styles)
+ .unwrap_or_else(|| match TextElem::dir_in(styles) {
+ Dir::LTR => Binding::Left,
+ _ => Binding::Right,
+ });
+
+ // Construct the numbering (for header or footer).
+ let numbering_marginal = numbering.as_ref().map(|numbering| {
+ let both = match numbering {
+ Numbering::Pattern(pattern) => pattern.pieces() >= 2,
+ Numbering::Func(_) => true,
+ };
+
+ let mut counter = CounterDisplayElem::new(
+ Counter::new(CounterKey::Page),
+ Smart::Custom(numbering.clone()),
+ both,
+ )
+ .pack()
+ .spanned(page.span());
+
+ // We interpret the Y alignment as selecting header or footer
+ // and then ignore it for aligning the actual number.
+ if let Some(x) = number_align.x() {
+ counter = counter.aligned(x.into());
+ }
+
+ counter
+ });
+
+ let header = page.header(styles);
+ let footer = page.footer(styles);
+ let (header, footer) = if matches!(number_align.y(), Some(OuterVAlignment::Top)) {
+ (header.as_ref().unwrap_or(&numbering_marginal), footer.as_ref().unwrap_or(&None))
+ } else {
+ (header.as_ref().unwrap_or(&None), footer.as_ref().unwrap_or(&numbering_marginal))
+ };
+
+ // Post-process pages.
+ let mut pages = Vec::with_capacity(frames.len());
+ for mut frame in frames {
+ // The padded width of the page's content without margins.
+ let pw = frame.width();
+
+ // If two sided, left becomes inside and right becomes outside.
+ // Thus, for left-bound pages, we want to swap on even pages and
+ // for right-bound pages, we want to swap on odd pages.
+ let mut margin = margin;
+ if two_sided && binding.swap(page_counter.physical()) {
+ std::mem::swap(&mut margin.left, &mut margin.right);
+ }
+
+ // Realize margins.
+ frame.set_size(frame.size() + margin.sum_by_axis());
+ frame.translate(Point::new(margin.left, margin.top));
+
+ // The page size with margins.
+ let size = frame.size();
+
+ // Realize overlays.
+ for marginal in [header, footer, background, foreground] {
+ let Some(content) = marginal.as_ref() else { continue };
+
+ let (pos, area, align);
+ if ptr::eq(marginal, header) {
+ let ascent = header_ascent.relative_to(margin.top);
+ pos = Point::with_x(margin.left);
+ area = Size::new(pw, margin.top - ascent);
+ align = Alignment::BOTTOM;
+ } else if ptr::eq(marginal, footer) {
+ let descent = footer_descent.relative_to(margin.bottom);
+ pos = Point::new(margin.left, size.y - margin.bottom + descent);
+ area = Size::new(pw, margin.bottom - descent);
+ align = Alignment::TOP;
+ } else {
+ pos = Point::zero();
+ area = size;
+ align = HAlignment::Center + VAlignment::Horizon;
+ };
+
+ let aligned = content.clone().styled(AlignElem::set_alignment(align));
+ let sub = layout_frame(
+ engine,
+ &aligned,
+ locator.next(&content.span()),
+ styles,
+ Region::new(area, Axes::splat(true)),
+ )?;
+
+ if ptr::eq(marginal, header) || ptr::eq(marginal, background) {
+ frame.prepend_frame(pos, sub);
+ } else {
+ frame.push_frame(pos, sub);
+ }
+ }
+
+ page_counter.visit(engine, &frame)?;
+ pages.push(Page {
+ frame,
+ fill: fill.clone(),
+ numbering: numbering.clone(),
+ number: page_counter.logical(),
+ });
+
+ page_counter.step();
+ }
+
+ Ok(pages)
+}
+
+/// Layout content into a single region.
+pub fn layout_frame(
+ engine: &mut Engine,
+ content: &Content,
+ locator: Locator,
+ styles: StyleChain,
+ region: Region,
+) -> SourceResult<Frame> {
+ layout_fragment(engine, content, locator, styles, region.into())
+ .map(Fragment::into_frame)
+}
+
+/// Layout content into multiple regions.
+///
+/// When just layouting into a single region, prefer [`layout_frame`].
+pub fn layout_fragment(
+ engine: &mut Engine,
+ content: &Content,
+ locator: Locator,
+ styles: StyleChain,
+ regions: Regions,
+) -> SourceResult<Fragment> {
+ layout_fragment_impl(
+ engine.world,
+ engine.introspector,
+ engine.traced,
+ TrackedMut::reborrow_mut(&mut engine.sink),
+ engine.route.track(),
+ content,
+ locator.track(),
+ styles,
+ regions,
+ )
+}
+
+/// The internal implementation of [`layout_fragment`].
+#[allow(clippy::too_many_arguments)]
+#[comemo::memoize]
+fn layout_fragment_impl(
+ world: Tracked<dyn World + '_>,
+ introspector: Tracked<Introspector>,
+ traced: Tracked<Traced>,
+ sink: TrackedMut<Sink>,
+ route: Tracked<Route>,
+ content: &Content,
+ locator: Tracked<Locator>,
+ styles: StyleChain,
+ regions: Regions,
+) -> SourceResult<Fragment> {
+ let link = LocatorLink::new(locator);
+ let mut locator = Locator::link(&link).split();
+ let mut engine = Engine {
+ world,
+ introspector,
+ traced,
+ sink,
+ route: Route::extend(route),
+ };
+
+ if !engine.route.within(Route::MAX_LAYOUT_DEPTH) {
+ bail!(
+ content.span(), "maximum layout depth exceeded";
+ hint: "try to reduce the amount of nesting in your layout",
+ );
+ }
+
+ // If we are in a `PageElem`, this might already be a realized flow.
+ if let Some(flow) = content.to_packed::<FlowElem>() {
+ return FlowLayouter::new(&mut engine, flow, locator, &styles, regions).layout();
+ }
+
+ // Layout the content by first turning it into a `FlowElem` and then
+ // layouting that.
+ let arenas = Arenas::default();
+ let (flow, styles) =
+ realize_flow(&mut engine, &mut locator, &arenas, content, styles)?;
+
+ FlowLayouter::new(&mut engine, &flow, locator, &styles, regions).layout()
+}
-/// Arranges spacing, paragraphs and block-level elements into a flow.
+/// A collection of block-level layoutable elements. This is analogous to a
+/// paragraph, which is a collection of inline-level layoutable elements.
///
/// This element is responsible for layouting both the top-level content flow
-/// and the contents of boxes.
+/// and the contents of any containers.
#[elem(Debug, Construct)]
pub struct FlowElem {
/// The children that will be arranged into a flow.
@@ -40,19 +432,6 @@ impl Construct for FlowElem {
}
}
-impl Packed<FlowElem> {
- #[typst_macros::time(name = "flow", span = self.span())]
- pub fn layout(
- &self,
- engine: &mut Engine,
- locator: Locator,
- styles: StyleChain,
- regions: Regions,
- ) -> SourceResult<Fragment> {
- FlowLayouter::new(engine, self, locator, &styles, regions).layout()
- }
-}
-
impl Debug for FlowElem {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "Flow ")?;
@@ -169,7 +548,7 @@ impl<'a, 'e> FlowLayouter<'a, 'e> {
fn new(
engine: &'a mut Engine<'e>,
flow: &'a Packed<FlowElem>,
- locator: Locator<'a>,
+ locator: SplitLocator<'a>,
styles: &'a StyleChain<'a>,
mut regions: Regions<'a>,
) -> Self {
@@ -200,7 +579,7 @@ impl<'a, 'e> FlowLayouter<'a, 'e> {
engine,
flow,
root,
- locator: locator.split(),
+ locator,
styles,
regions,
expand,
@@ -272,6 +651,7 @@ impl<'a, 'e> FlowLayouter<'a, 'e> {
}
/// Layout a paragraph.
+ #[typst_macros::time(name = "par", span = par.span())]
fn handle_par(
&mut self,
par: &'a Packed<ParElem>,
@@ -286,16 +666,16 @@ impl<'a, 'e> FlowLayouter<'a, 'e> {
// not on the Y position.
let consecutive = self.last_was_par;
let locator = self.locator.next(&par.span());
- let lines = par
- .layout(
- self.engine,
- locator,
- styles,
- consecutive,
- self.regions.base(),
- self.regions.expand.x,
- )?
- .into_frames();
+ let lines = crate::layout::layout_inline(
+ self.engine,
+ &par.children,
+ locator,
+ styles,
+ consecutive,
+ self.regions.base(),
+ self.regions.expand.x,
+ )?
+ .into_frames();
// If the first line doesn’t fit in this region, then defer any
// previous sticky frame to the next region (if available)
@@ -440,14 +820,12 @@ impl<'a, 'e> FlowLayouter<'a, 'e> {
});
let y_align = alignment.map(|align| align.y().map(|y| y.resolve(styles)));
- let mut frame = placed
- .layout(
- self.engine,
- self.locator.next(&placed.span()),
- styles,
- self.regions.base(),
- )?
- .into_frame();
+ let mut frame = placed.layout(
+ self.engine,
+ self.locator.next(&placed.span()),
+ styles,
+ self.regions.base(),
+ )?;
frame.post_process(styles);
@@ -830,15 +1208,14 @@ impl<'a, 'e> FlowLayouter<'a, 'e> {
}
self.regions.size.y -= self.footnote_config.gap;
- let frames = FootnoteEntry::new(notes[k].clone())
- .pack()
- .layout(
- self.engine,
- Locator::synthesize(notes[k].location().unwrap()),
- *self.styles,
- self.regions.with_root(false),
- )?
- .into_frames();
+ let frames = layout_fragment(
+ self.engine,
+ &FootnoteEntry::new(notes[k].clone()).pack(),
+ Locator::synthesize(notes[k].location().unwrap()),
+ *self.styles,
+ self.regions.with_root(false),
+ )?
+ .into_frames();
// If the entries didn't fit, abort (to keep footnote and entry
// together).
@@ -882,13 +1259,12 @@ impl<'a, 'e> FlowLayouter<'a, 'e> {
/// Layout and save the footnote separator, typically a line.
fn layout_footnote_separator(&mut self) -> SourceResult<()> {
let expand = Axes::new(self.regions.expand.x, false);
- let pod = Regions::one(self.regions.base(), expand);
+ let pod = Region::new(self.regions.base(), expand);
let separator = &self.footnote_config.separator;
// FIXME: Shouldn't use `root()` here.
- let mut frame = separator
- .layout(self.engine, Locator::root(), *self.styles, pod)?
- .into_frame();
+ let mut frame =
+ layout_frame(self.engine, separator, Locator::root(), *self.styles, pod)?;
frame.size_mut().y += self.footnote_config.clearance;
frame.translate(Point::with_y(self.footnote_config.clearance));
diff --git a/crates/typst/src/layout/grid/cells.rs b/crates/typst/src/layout/grid/cells.rs
index e45d58d4..2e788d34 100644
--- a/crates/typst/src/layout/grid/cells.rs
+++ b/crates/typst/src/layout/grid/cells.rs
@@ -14,7 +14,8 @@ use crate::foundations::{
};
use crate::introspection::Locator;
use crate::layout::{
- Abs, Alignment, Axes, Fragment, Length, LinePosition, Regions, Rel, Sides, Sizing,
+ layout_fragment, Abs, Alignment, Axes, Fragment, Length, LinePosition, Regions, Rel,
+ Sides, Sizing,
};
use crate::syntax::Span;
use crate::utils::NonZeroExt;
@@ -220,7 +221,7 @@ impl<'a> Cell<'a> {
if disambiguator > 0 {
locator = locator.split().next_inner(disambiguator as u128);
}
- self.body.layout(engine, locator, styles, regions)
+ layout_fragment(engine, &self.body, locator, styles, regions)
}
}
diff --git a/crates/typst/src/layout/grid/layout.rs b/crates/typst/src/layout/grid/layout.rs
index 9c5d7c21..53eda0f0 100644
--- a/crates/typst/src/layout/grid/layout.rs
+++ b/crates/typst/src/layout/grid/layout.rs
@@ -11,7 +11,7 @@ use crate::engine::Engine;
use crate::foundations::{Resolve, StyleChain};
use crate::layout::{
Abs, Axes, Cell, CellGrid, Dir, Fr, Fragment, Frame, FrameItem, Length, Point,
- Regions, Rel, Size, Sizing,
+ Region, Regions, Rel, Size, Sizing,
};
use crate::syntax::Span;
use crate::text::TextElem;
@@ -843,8 +843,8 @@ impl<'a> GridLayouter<'a> {
let already_covered_width = self.cell_spanned_width(cell, parent.x);
let size = Size::new(available, height);
- let pod = Regions::one(size, Axes::splat(false));
- let frame = cell.layout(engine, 0, self.styles, pod)?.into_frame();
+ let pod = Region::new(size, Axes::splat(false));
+ let frame = cell.layout(engine, 0, self.styles, pod.into())?.into_frame();
resolved.set_max(frame.width() - already_covered_width);
}
@@ -1062,7 +1062,7 @@ impl<'a> GridLayouter<'a> {
// Force cell to fit into a single region when the row is
// unbreakable, even when it is a breakable rowspan, as a best
// effort.
- let mut pod = Regions::one(size, self.regions.expand);
+ let mut pod: Regions = Region::new(size, self.regions.expand).into();
pod.full = measurement_data.full;
if measurement_data.frames_in_previous_regions > 0 {
@@ -1244,7 +1244,7 @@ impl<'a> GridLayouter<'a> {
if cell.rowspan.get() == 1 {
let width = self.cell_spanned_width(cell, x);
let size = Size::new(width, height);
- let mut pod = Regions::one(size, Axes::splat(true));
+ let mut pod: Regions = Region::new(size, Axes::splat(true)).into();
if self.grid.rows[y] == Sizing::Auto
&& self.unbreakable_rows_left == 0
{
@@ -1296,7 +1296,7 @@ impl<'a> GridLayouter<'a> {
// Prepare regions.
let size = Size::new(self.width, heights[0]);
- let mut pod = Regions::one(size, Axes::splat(true));
+ let mut pod: Regions = Region::new(size, Axes::splat(true)).into();
pod.full = self.regions.full;
pod.backlog = &heights[1..];
diff --git a/crates/typst/src/layout/grid/mod.rs b/crates/typst/src/layout/grid/mod.rs
index 326e356a..08257a5c 100644
--- a/crates/typst/src/layout/grid/mod.rs
+++ b/crates/typst/src/layout/grid/mod.rs
@@ -967,7 +967,7 @@ impl From<Content> for GridCell {
}
/// Function with common code to display a grid cell or table cell.
-pub fn show_grid_cell(
+pub(crate) fn show_grid_cell(
mut body: Content,
inset: Smart<Sides<Option<Rel<Length>>>>,
align: Smart<Alignment>,
diff --git a/crates/typst/src/layout/grid/rowspans.rs b/crates/typst/src/layout/grid/rowspans.rs
index 85ec49c9..91e7d8ef 100644
--- a/crates/typst/src/layout/grid/rowspans.rs
+++ b/crates/typst/src/layout/grid/rowspans.rs
@@ -3,7 +3,9 @@ use super::repeated::Repeatable;
use crate::diag::SourceResult;
use crate::engine::Engine;
use crate::foundations::Resolve;
-use crate::layout::{Abs, Axes, Cell, Frame, GridLayouter, Point, Regions, Size, Sizing};
+use crate::layout::{
+ Abs, Axes, Cell, Frame, GridLayouter, Point, Region, Regions, Size, Sizing,
+};
use crate::utils::MaybeReverseIter;
/// All information needed to layout a single rowspan.
@@ -123,7 +125,7 @@ impl<'a> GridLayouter<'a> {
// Prepare regions.
let size = Size::new(width, *first_height);
- let mut pod = Regions::one(size, Axes::splat(true));
+ let mut pod: Regions = Region::new(size, Axes::splat(true)).into();
pod.backlog = backlog;
if !is_effectively_unbreakable
diff --git a/crates/typst/src/layout/inline/mod.rs b/crates/typst/src/layout/inline/mod.rs
index 821b4f57..44b6ee3c 100644
--- a/crates/typst/src/layout/inline/mod.rs
+++ b/crates/typst/src/layout/inline/mod.rs
@@ -30,54 +30,15 @@ type Range = std::ops::Range<usize>;
/// Layouts content inline.
pub(crate) fn layout_inline(
- children: &StyleVec,
engine: &mut Engine,
+ children: &StyleVec,
locator: Locator,
styles: StyleChain,
consecutive: bool,
region: Size,
expand: bool,
) -> SourceResult<Fragment> {
- #[comemo::memoize]
- #[allow(clippy::too_many_arguments)]
- fn cached(
- children: &StyleVec,
- world: Tracked<dyn World + '_>,
- introspector: Tracked<Introspector>,
- traced: Tracked<Traced>,
- sink: TrackedMut<Sink>,
- route: Tracked<Route>,
- locator: Tracked<Locator>,
- styles: StyleChain,
- consecutive: bool,
- region: Size,
- expand: bool,
- ) -> SourceResult<Fragment> {
- let link = LocatorLink::new(locator);
- let locator = Locator::link(&link);
- let mut engine = Engine {
- world,
- introspector,
- traced,
- sink,
- route: Route::extend(route),
- };
-
- // Collect all text into one string for BiDi analysis.
- let (text, segments, spans) =
- collect(children, &mut engine, locator, &styles, region, consecutive)?;
-
- // Perform BiDi analysis and then prepares paragraph layout.
- let p = prepare(&mut engine, children, &text, segments, spans, styles)?;
-
- // Break the paragraph into lines.
- let lines = linebreak(&engine, &p, region.x - p.hang);
-
- // Turn the selected lines into frames.
- finalize(&mut engine, &p, &lines, styles, region, expand)
- }
-
- cached(
+ layout_inline_impl(
children,
engine.world,
engine.introspector,
@@ -91,3 +52,43 @@ pub(crate) fn layout_inline(
expand,
)
}
+
+/// The internal, memoized implementation of `layout_inline`.
+#[comemo::memoize]
+#[allow(clippy::too_many_arguments)]
+fn layout_inline_impl(
+ children: &StyleVec,
+ world: Tracked<dyn World + '_>,
+ introspector: Tracked<Introspector>,
+ traced: Tracked<Traced>,
+ sink: TrackedMut<Sink>,
+ route: Tracked<Route>,
+ locator: Tracked<Locator>,
+ styles: StyleChain,
+ consecutive: bool,
+ region: Size,
+ expand: bool,
+) -> SourceResult<Fragment> {
+ let link = LocatorLink::new(locator);
+ let locator = Locator::link(&link);
+ let mut engine = Engine {
+ world,
+ introspector,
+ traced,
+ sink,
+ route: Route::extend(route),
+ };
+
+ // Collect all text into one string for BiDi analysis.
+ let (text, segments, spans) =
+ collect(children, &mut engine, locator, &styles, region, consecutive)?;
+
+ // Perform BiDi analysis and then prepares paragraph layout.
+ let p = prepare(&mut engine, children, &text, segments, spans, styles)?;
+
+ // Break the paragraph into lines.
+ let lines = linebreak(&engine, &p, region.x - p.hang);
+
+ // Turn the selected lines into frames.
+ finalize(&mut engine, &p, &lines, styles, region, expand)
+}
diff --git a/crates/typst/src/layout/layout.rs b/crates/typst/src/layout/layout.rs
index efe5d124..bf662740 100644
--- a/crates/typst/src/layout/layout.rs
+++ b/crates/typst/src/layout/layout.rs
@@ -6,7 +6,7 @@ use crate::foundations::{
dict, elem, func, Content, Context, Func, NativeElement, Packed, Show, StyleChain,
};
use crate::introspection::Locatable;
-use crate::layout::{BlockElem, Size};
+use crate::layout::{layout_fragment, BlockElem, Size};
use crate::syntax::Span;
/// Provides access to the current outer container's (or page's, if none)
@@ -92,7 +92,7 @@ impl Show for Packed<LayoutElem> {
[dict! { "width" => x, "height" => y }],
)?
.display();
- result.layout(engine, locator, styles, regions)
+ layout_fragment(engine, &result, locator, styles, regions)
},
)
.pack()
diff --git a/crates/typst/src/layout/measure.rs b/crates/typst/src/layout/measure.rs
index cb08e591..92d4c5c3 100644
--- a/crates/typst/src/layout/measure.rs
+++ b/crates/typst/src/layout/measure.rs
@@ -6,7 +6,7 @@ use crate::foundations::{
dict, func, Content, Context, Dict, Resolve, Smart, StyleChain, Styles,
};
use crate::introspection::{Locator, LocatorLink};
-use crate::layout::{Abs, Axes, Length, Regions, Size};
+use crate::layout::{layout_frame, Abs, Axes, Length, Region, Size};
use crate::syntax::Span;
/// Measures the layouted size of content.
@@ -93,7 +93,7 @@ pub fn measure(
};
// Create a pod region with the available space.
- let pod = Regions::one(
+ let pod = Region::new(
Axes::new(
width.resolve(styles).unwrap_or(Abs::inf()),
height.resolve(styles).unwrap_or(Abs::inf()),
@@ -109,7 +109,7 @@ pub fn measure(
let link = LocatorLink::measure(here);
let locator = Locator::link(&link);
- let frame = content.layout(engine, locator, styles, pod)?.into_frame();
+ let frame = layout_frame(engine, &content, locator, styles, pod)?;
let Size { x, y } = frame.size();
Ok(dict! { "width" => x, "height" => y })
}
diff --git a/crates/typst/src/layout/mod.rs b/crates/typst/src/layout/mod.rs
index f7f5c5b3..8f3dc7c5 100644
--- a/crates/typst/src/layout/mod.rs
+++ b/crates/typst/src/layout/mod.rs
@@ -67,17 +67,9 @@ pub use self::spacing::*;
pub use self::stack::*;
pub use self::transform::*;
-pub(crate) use self::inline::*;
+pub(crate) use self::inline::layout_inline;
-use comemo::{Track, Tracked, TrackedMut};
-
-use crate::diag::{bail, SourceResult};
-use crate::engine::{Engine, Route, Sink, Traced};
-use crate::foundations::{category, Category, Content, Scope, StyleChain};
-use crate::introspection::{Introspector, Locator, LocatorLink};
-use crate::model::Document;
-use crate::realize::{realize_doc, realize_flow, Arenas};
-use crate::World;
+use crate::foundations::{category, Category, Scope};
/// Arranging elements on the page in different ways.
///
@@ -116,117 +108,3 @@ pub fn define(global: &mut Scope) {
global.define_func::<measure>();
global.define_func::<layout>();
}
-
-impl Content {
- /// Layout the content into a document.
- ///
- /// This first realizes the content into a
- /// [`DocumentElem`][crate::model::DocumentElem], which is then laid out. In
- /// contrast to [`layout`](Self::layout()), this does not take regions since
- /// the regions are defined by the page configuration in the content and
- /// style chain.
- pub fn layout_document(
- &self,
- engine: &mut Engine,
- styles: StyleChain,
- ) -> SourceResult<Document> {
- #[comemo::memoize]
- fn cached(
- content: &Content,
- world: Tracked<dyn World + '_>,
- introspector: Tracked<Introspector>,
- traced: Tracked<Traced>,
- sink: TrackedMut<Sink>,
- route: Tracked<Route>,
- styles: StyleChain,
- ) -> SourceResult<Document> {
- let mut locator = Locator::root().split();
- let mut engine = Engine {
- world,
- introspector,
- traced,
- sink,
- route: Route::extend(route).unnested(),
- };
- let arenas = Arenas::default();
- let (document, styles, info) =
- realize_doc(&mut engine, locator.next(&()), &arenas, content, styles)?;
- document.layout(&mut engine, locator.next(&()), styles, info)
- }
-
- cached(
- self,
- engine.world,
- engine.introspector,
- engine.traced,
- TrackedMut::reborrow_mut(&mut engine.sink),
- engine.route.track(),
- styles,
- )
- }
-
- /// Layout the content into the given regions.
- pub fn layout(
- &self,
- engine: &mut Engine,
- locator: Locator,
- styles: StyleChain,
- regions: Regions,
- ) -> SourceResult<Fragment> {
- #[allow(clippy::too_many_arguments)]
- #[comemo::memoize]
- fn cached(
- content: &Content,
- world: Tracked<dyn World + '_>,
- introspector: Tracked<Introspector>,
- traced: Tracked<Traced>,
- sink: TrackedMut<Sink>,
- route: Tracked<Route>,
- locator: Tracked<Locator>,
- styles: StyleChain,
- regions: Regions,
- ) -> SourceResult<Fragment> {
- let link = LocatorLink::new(locator);
- let locator = Locator::link(&link);
- let mut engine = Engine {
- world,
- introspector,
- traced,
- sink,
- route: Route::extend(route),
- };
-
- if !engine.route.within(Route::MAX_LAYOUT_DEPTH) {
- bail!(
- content.span(), "maximum layout depth exceeded";
- hint: "try to reduce the amount of nesting in your layout",
- );
- }
-
- // If we are in a `PageElem`, this might already be a realized flow.
- if let Some(flow) = content.to_packed::<FlowElem>() {
- return flow.layout(&mut engine, locator, styles, regions);
- }
-
- // Layout the content by first turning it into a `FlowElem` and then
- // layouting that.
- let mut locator = locator.split();
- let arenas = Arenas::default();
- let (flow, styles) =
- realize_flow(&mut engine, locator.next(&()), &arenas, content, styles)?;
- flow.layout(&mut engine, locator.next(&()), styles, regions)
- }
-
- cached(
- self,
- engine.world,
- engine.introspector,
- engine.traced,
- TrackedMut::reborrow_mut(&mut engine.sink),
- engine.route.track(),
- locator.track(),
- styles,
- regions,
- )
- }
-}
diff --git a/crates/typst/src/layout/pad.rs b/crates/typst/src/layout/pad.rs
index 814ccdc5..f1b69a5c 100644
--- a/crates/typst/src/layout/pad.rs
+++ b/crates/typst/src/layout/pad.rs
@@ -5,7 +5,8 @@ use crate::foundations::{
};
use crate::introspection::Locator;
use crate::layout::{
- Abs, BlockElem, Fragment, Frame, Length, Point, Regions, Rel, Sides, Size,
+ layout_fragment, Abs, BlockElem, Fragment, Frame, Length, Point, Regions, Rel, Sides,
+ Size,
};
/// Adds spacing around content.
@@ -91,7 +92,7 @@ fn layout_pad(
let pod = regions.map(&mut backlog, |size| shrink(size, &padding));
// Layout child into padded regions.
- let mut fragment = elem.body().layout(engine, locator, styles, pod)?;
+ let mut fragment = layout_fragment(engine, &elem.body, locator, styles, pod)?;
for frame in &mut fragment {
grow(frame, &padding);
diff --git a/crates/typst/src/layout/page.rs b/crates/typst/src/layout/page.rs
index ca2a0ce9..f52f59e9 100644
--- a/crates/typst/src/layout/page.rs
+++ b/crates/typst/src/layout/page.rs
@@ -1,7 +1,6 @@
use std::borrow::Cow;
use std::num::NonZeroUsize;
use std::ops::RangeInclusive;
-use std::ptr;
use std::str::FromStr;
use comemo::Track;
@@ -9,21 +8,15 @@ use comemo::Track;
use crate::diag::{bail, SourceResult};
use crate::engine::Engine;
use crate::foundations::{
- cast, elem, AutoValue, Cast, Content, Context, Dict, Fold, Func, NativeElement,
- Packed, Resolve, Smart, StyleChain, Value,
-};
-use crate::introspection::{
- Counter, CounterDisplayElem, CounterKey, Locator, ManualPageCounter, SplitLocator,
+ cast, elem, AutoValue, Cast, Content, Context, Dict, Fold, Func, Smart, StyleChain,
+ Value,
};
use crate::layout::{
- Abs, AlignElem, Alignment, Axes, ColumnsElem, Dir, Frame, HAlignment, Length,
- OuterVAlignment, Point, Ratio, Regions, Rel, Sides, Size, SpecificAlignment,
- VAlignment,
+ Abs, Alignment, Frame, HAlignment, Length, OuterVAlignment, Ratio, Rel, Sides,
+ SpecificAlignment,
};
-
use crate::model::Numbering;
-use crate::text::TextElem;
-use crate::utils::{NonZeroExt, Numeric, Scalar};
+use crate::utils::{NonZeroExt, Scalar};
use crate::visualize::{Color, Paint};
/// Layouts its child onto one or multiple pages.
@@ -348,236 +341,6 @@ pub struct PageElem {
pub clear_to: Option<Parity>,
}
-impl Packed<PageElem> {
- /// A document can consist of multiple `PageElem`s, one per run of pages
- /// with equal properties (not one per actual output page!). The `number` is
- /// the physical page number of the first page of this run. It is mutated
- /// while we post-process the pages in this function. This function returns
- /// a fragment consisting of multiple frames, one per output page of this
- /// page run.
- #[typst_macros::time(name = "page", span = self.span())]
- pub fn layout<'a>(
- &'a self,
- engine: &mut Engine,
- locator: Locator<'a>,
- styles: StyleChain<'a>,
- extend_to: Option<Parity>,
- ) -> SourceResult<PageLayout<'a>> {
- let mut locator = locator.split();
-
- // When one of the lengths is infinite the page fits its content along
- // that axis.
- let width = self.width(styles).unwrap_or(Abs::inf());
- let height = self.height(styles).unwrap_or(Abs::inf());
- let mut size = Size::new(width, height);
- if self.flipped(styles) {
- std::mem::swap(&mut size.x, &mut size.y);
- }
-
- let mut min = width.min(height);
- if !min.is_finite() {
- min = Paper::A4.width();
- }
-
- // Determine the margins.
- let default = Rel::<Length>::from((2.5 / 21.0) * min);
- let margin = self.margin(styles);
- let two_sided = margin.two_sided.unwrap_or(false);
- let margin = margin
- .sides
- .map(|side| side.and_then(Smart::custom).unwrap_or(default))
- .resolve(styles)
- .relative_to(size);
-
- // Realize columns.
- let mut child = self.body().clone();
- let columns = self.columns(styles);
- if columns.get() > 1 {
- child = ColumnsElem::new(child)
- .with_count(columns)
- .pack()
- .spanned(self.span());
- }
-
- let area = size - margin.sum_by_axis();
- let mut regions = Regions::repeat(area, area.map(Abs::is_finite));
- regions.root = true;
-
- // Layout the child.
- let frames = child
- .layout(engine, locator.next(&self.span()), styles, regions)?
- .into_frames();
-
- Ok(PageLayout {
- page: self,
- locator,
- styles,
- extend_to,
- area,
- margin,
- two_sided,
- frames,
- })
- }
-}
-
-/// A prepared layout of a page run that can be finalized with access to the
-/// page counter.
-pub struct PageLayout<'a> {
- page: &'a Packed<PageElem>,
- locator: SplitLocator<'a>,
- styles: StyleChain<'a>,
- extend_to: Option<Parity>,
- area: Size,
- margin: Sides<Abs>,
- two_sided: bool,
- frames: Vec<Frame>,
-}
-
-impl PageLayout<'_> {
- /// Finalize the layout with access to the next page counter.
- #[typst_macros::time(name = "finalize page", span = self.page.span())]
- pub fn finalize(
- mut self,
- engine: &mut Engine,
- page_counter: &mut ManualPageCounter,
- ) -> SourceResult<Vec<Page>> {
- let styles = self.styles;
-
- // Align the child to the pagebreak's parity.
- // Check for page count after adding the pending frames
- if self.extend_to.is_some_and(|p| {
- !p.matches(page_counter.physical().get() + self.frames.len())
- }) {
- // Insert empty page after the current pages.
- let size = self.area.map(Abs::is_finite).select(self.area, Size::zero());
- self.frames.push(Frame::hard(size));
- }
-
- let fill = self.page.fill(styles);
- let foreground = self.page.foreground(styles);
- let background = self.page.background(styles);
- let header_ascent = self.page.header_ascent(styles);
- let footer_descent = self.page.footer_descent(styles);
- let numbering = self.page.numbering(styles);
- let number_align = self.page.number_align(styles);
- let binding =
- self.page
- .binding(styles)
- .unwrap_or_else(|| match TextElem::dir_in(styles) {
- Dir::LTR => Binding::Left,
- _ => Binding::Right,
- });
-
- // Construct the numbering (for header or footer).
- let numbering_marginal = numbering.as_ref().map(|numbering| {
- let both = match numbering {
- Numbering::Pattern(pattern) => pattern.pieces() >= 2,
- Numbering::Func(_) => true,
- };
-
- let mut counter = CounterDisplayElem::new(
- Counter::new(CounterKey::Page),
- Smart::Custom(numbering.clone()),
- both,
- )
- .pack()
- .spanned(self.page.span());
-
- // We interpret the Y alignment as selecting header or footer
- // and then ignore it for aligning the actual number.
- if let Some(x) = number_align.x() {
- counter = counter.aligned(x.into());
- }
-
- counter
- });
-
- let header = self.page.header(styles);
- let footer = self.page.footer(styles);
- let (header, footer) = if matches!(number_align.y(), Some(OuterVAlignment::Top)) {
- (
- header.as_ref().unwrap_or(&numbering_marginal),
- footer.as_ref().unwrap_or(&None),
- )
- } else {
- (
- header.as_ref().unwrap_or(&None),
- footer.as_ref().unwrap_or(&numbering_marginal),
- )
- };
-
- // Post-process pages.
- let mut pages = Vec::with_capacity(self.frames.len());
- for mut frame in self.frames {
- // The padded width of the page's content without margins.
- let pw = frame.width();
-
- // If two sided, left becomes inside and right becomes outside.
- // Thus, for left-bound pages, we want to swap on even pages and
- // for right-bound pages, we want to swap on odd pages.
- let mut margin = self.margin;
- if self.two_sided && binding.swap(page_counter.physical()) {
- std::mem::swap(&mut margin.left, &mut margin.right);
- }
-
- // Realize margins.
- frame.set_size(frame.size() + margin.sum_by_axis());
- frame.translate(Point::new(margin.left, margin.top));
-
- // The page size with margins.
- let size = frame.size();
-
- // Realize overlays.
- for marginal in [header, footer, background, foreground] {
- let Some(content) = marginal.as_ref() else { continue };
-
- let (pos, area, align);
- if ptr::eq(marginal, header) {
- let ascent = header_ascent.relative_to(margin.top);
- pos = Point::with_x(margin.left);
- area = Size::new(pw, margin.top - ascent);
- align = Alignment::BOTTOM;
- } else if ptr::eq(marginal, footer) {
- let descent = footer_descent.relative_to(margin.bottom);
- pos = Point::new(margin.left, size.y - margin.bottom + descent);
- area = Size::new(pw, margin.bottom - descent);
- align = Alignment::TOP;
- } else {
- pos = Point::zero();
- area = size;
- align = HAlignment::Center + VAlignment::Horizon;
- };
-
- let pod = Regions::one(area, Axes::splat(true));
- let sub = content
- .clone()
- .styled(AlignElem::set_alignment(align))
- .layout(engine, self.locator.next(&content.span()), styles, pod)?
- .into_frame();
-
- if ptr::eq(marginal, header) || ptr::eq(marginal, background) {
- frame.prepend_frame(pos, sub);
- } else {
- frame.push_frame(pos, sub);
- }
- }
-
- page_counter.visit(engine, &frame)?;
- pages.push(Page {
- frame,
- fill: fill.clone(),
- numbering: numbering.clone(),
- number: page_counter.logical(),
- });
-
- page_counter.step();
- }
-
- Ok(pages)
- }
-}
-
/// A finished page.
#[derive(Debug, Clone)]
pub struct Page {
@@ -736,7 +499,7 @@ pub enum Binding {
impl Binding {
/// Whether to swap left and right margin for the page with this number.
- fn swap(self, number: NonZeroUsize) -> bool {
+ pub fn swap(self, number: NonZeroUsize) -> bool {
match self {
// Left-bound must swap on even pages
// (because it is correct on the first page).
@@ -798,14 +561,18 @@ cast! {
v: Func => Self::Func(v),
}
-/// A list of page ranges to be exported. The ranges are one-indexed.
-/// For example, `1..=3` indicates the first, second and third pages should be
-/// exported.
+/// A list of page ranges to be exported.
+
pub struct PageRanges(Vec<PageRange>);
+/// A range of pages to export.
+///
+/// The range is one-indexed. For example, `1..=3` indicates the first, second
+/// and third pages should be exported.
pub type PageRange = RangeInclusive<Option<NonZeroUsize>>;
impl PageRanges {
+ /// Create new page ranges.
pub fn new(ranges: Vec<PageRange>) -> Self {
Self(ranges)
}
@@ -875,7 +642,7 @@ pub enum Parity {
impl Parity {
/// Whether the given number matches the parity.
- fn matches(self, number: usize) -> bool {
+ pub fn matches(self, number: usize) -> bool {
match self {
Self::Even => number % 2 == 0,
Self::Odd => number % 2 == 1,
diff --git a/crates/typst/src/layout/place.rs b/crates/typst/src/layout/place.rs
index be211c15..51a1f5bf 100644
--- a/crates/typst/src/layout/place.rs
+++ b/crates/typst/src/layout/place.rs
@@ -3,7 +3,7 @@ use crate::engine::Engine;
use crate::foundations::{elem, scope, Content, Packed, Smart, StyleChain, Unlabellable};
use crate::introspection::Locator;
use crate::layout::{
- Alignment, Axes, Em, Fragment, Length, Regions, Rel, Size, VAlignment,
+ layout_frame, Alignment, Axes, Em, Frame, Length, Region, Rel, Size, VAlignment,
};
use crate::realize::{Behave, Behaviour};
@@ -112,7 +112,7 @@ impl Packed<PlaceElem> {
locator: Locator,
styles: StyleChain,
base: Size,
- ) -> SourceResult<Fragment> {
+ ) -> SourceResult<Frame> {
// The pod is the base area of the region because for absolute
// placement we don't really care about the already used area.
let float = self.float(styles);
@@ -135,9 +135,8 @@ impl Packed<PlaceElem> {
.clone()
.aligned(alignment.unwrap_or_else(|| Alignment::CENTER));
- let pod = Regions::one(base, Axes::splat(false));
- let frame = child.layout(engine, locator, styles, pod)?.into_frame();
- Ok(Fragment::frame(frame))
+ let pod = Region::new(base, Axes::splat(false));
+ layout_frame(engine, &child, locator, styles, pod)
}
}
diff --git a/crates/typst/src/layout/regions.rs b/crates/typst/src/layout/regions.rs
index 6280632d..7ff2e1c4 100644
--- a/crates/typst/src/layout/regions.rs
+++ b/crates/typst/src/layout/regions.rs
@@ -17,10 +17,18 @@ impl Region {
pub fn new(size: Size, expand: Axes<bool>) -> Self {
Self { size, expand }
}
+}
- /// Turns this into a region sequence.
- pub fn into_regions(self) -> Regions<'static> {
- Regions::one(self.size, self.expand)
+impl From<Region> for Regions<'_> {
+ fn from(region: Region) -> Self {
+ Regions {
+ size: region.size,
+ expand: region.expand,
+ full: region.size.y,
+ backlog: &[],
+ last: None,
+ root: false,
+ }
}
}
@@ -35,6 +43,9 @@ impl Region {
pub struct Regions<'a> {
/// The remaining size of the first region.
pub size: Size,
+ /// Whether elements should expand to fill the regions instead of shrinking
+ /// to fit the content.
+ pub expand: Axes<bool>,
/// The full height of the region for relative sizing.
pub full: Abs,
/// The height of followup regions. The width is the same for all regions.
@@ -42,9 +53,6 @@ pub struct Regions<'a> {
/// The height of the final region that is repeated once the backlog is
/// drained. The width is the same for all regions.
pub last: Option<Abs>,
- /// Whether elements should expand to fill the regions instead of shrinking
- /// to fit the content.
- pub expand: Axes<bool>,
/// Whether these are the root regions or direct descendants.
///
/// True for the padded page regions and columns directly in the page,
@@ -53,18 +61,6 @@ pub struct Regions<'a> {
}
impl Regions<'_> {
- /// Create a new region sequence with exactly one region.
- pub fn one(size: Size, expand: Axes<bool>) -> Self {
- Self {
- size,
- full: size.y,
- backlog: &[],
- last: None,
- expand,
- root: false,
- }
- }
-
/// Create a new sequence of same-size regions that repeats indefinitely.
pub fn repeat(size: Size, expand: Axes<bool>) -> Self {
Self {
diff --git a/crates/typst/src/layout/repeat.rs b/crates/typst/src/layout/repeat.rs
index 60a3e4c7..66a6f923 100644
--- a/crates/typst/src/layout/repeat.rs
+++ b/crates/typst/src/layout/repeat.rs
@@ -5,7 +5,7 @@ use crate::foundations::{
};
use crate::introspection::Locator;
use crate::layout::{
- Abs, AlignElem, Axes, BlockElem, Frame, Length, Point, Region, Regions, Size,
+ layout_frame, Abs, AlignElem, Axes, BlockElem, Frame, Length, Point, Region, Size,
};
use crate::utils::Numeric;
@@ -63,8 +63,8 @@ fn layout_repeat(
styles: StyleChain,
region: Region,
) -> SourceResult<Frame> {
- let pod = Regions::one(region.size, Axes::new(false, false));
- let piece = elem.body().layout(engine, locator, styles, pod)?.into_frame();
+ let pod = Region::new(region.size, Axes::new(false, false));
+ let piece = layout_frame(engine, &elem.body, locator, styles, pod)?;
let size = Size::new(region.size.x, piece.height());
if !size.is_finite() {
diff --git a/crates/typst/src/layout/stack.rs b/crates/typst/src/layout/stack.rs
index 3d8002ab..6f1aa534 100644
--- a/crates/typst/src/layout/stack.rs
+++ b/crates/typst/src/layout/stack.rs
@@ -8,8 +8,8 @@ use crate::foundations::{
};
use crate::introspection::{Locator, SplitLocator};
use crate::layout::{
- Abs, AlignElem, Axes, Axis, BlockElem, Dir, FixedAlignment, Fr, Fragment, Frame,
- HElem, Point, Regions, Size, Spacing, VElem,
+ layout_fragment, Abs, AlignElem, Axes, Axis, BlockElem, Dir, FixedAlignment, Fr,
+ Fragment, Frame, HElem, Point, Regions, Size, Spacing, VElem,
};
use crate::utils::{Get, Numeric};
@@ -257,8 +257,9 @@ impl<'a> StackLayouter<'a> {
}
.resolve(styles);
- let fragment = block.layout(
+ let fragment = layout_fragment(
engine,
+ block,
self.locator.next(&block.span()),
styles,
self.regions,
diff --git a/crates/typst/src/layout/transform.rs b/crates/typst/src/layout/transform.rs
index dcb178fd..2c718193 100644
--- a/crates/typst/src/layout/transform.rs
+++ b/crates/typst/src/layout/transform.rs
@@ -9,8 +9,8 @@ use crate::foundations::{
};
use crate::introspection::Locator;
use crate::layout::{
- Abs, Alignment, Angle, Axes, BlockElem, FixedAlignment, Frame, HAlignment, Length,
- Point, Ratio, Region, Regions, Rel, Size, VAlignment,
+ layout_frame, Abs, Alignment, Angle, Axes, BlockElem, FixedAlignment, Frame,
+ HAlignment, Length, Point, Ratio, Region, Rel, Size, VAlignment,
};
use crate::utils::Numeric;
@@ -62,10 +62,7 @@ fn layout_move(
styles: StyleChain,
region: Region,
) -> SourceResult<Frame> {
- let mut frame = elem
- .body()
- .layout(engine, locator, styles, region.into_regions())?
- .into_frame();
+ let mut frame = layout_frame(engine, &elem.body, locator, styles, region)?;
let delta = Axes::new(elem.dx(styles), elem.dy(styles)).resolve(styles);
let delta = delta.zip_map(region.size, Rel::relative_to);
frame.translate(delta.to_point());
@@ -299,8 +296,8 @@ impl Packed<ScaleElem> {
}
let size = Lazy::new(|| {
- let pod = Regions::one(container, Axes::splat(false));
- let frame = self.body().layout(engine, locator, styles, pod)?.into_frame();
+ let pod = Region::new(container, Axes::splat(false));
+ let frame = layout_frame(engine, &self.body, locator, styles, pod)?;
SourceResult::Ok(frame.size())
});
@@ -478,12 +475,12 @@ fn measure_and_layout(
) -> SourceResult<Frame> {
if reflow {
// Measure the size of the body.
- let pod = Regions::one(size, Axes::splat(false));
- let frame = body.layout(engine, locator.relayout(), styles, pod)?.into_frame();
+ let pod = Region::new(size, Axes::splat(false));
+ let frame = layout_frame(engine, body, locator.relayout(), styles, pod)?;
// Actually perform the layout.
- let pod = Regions::one(frame.size(), Axes::splat(true));
- let mut frame = body.layout(engine, locator, styles, pod)?.into_frame();
+ let pod = Region::new(frame.size(), Axes::splat(true));
+ let mut frame = layout_frame(engine, body, locator, styles, pod)?;
let Axes { x, y } = align.zip_map(frame.size(), FixedAlignment::position);
// Compute the transform.
@@ -499,9 +496,7 @@ fn measure_and_layout(
Ok(frame)
} else {
// Layout the body.
- let mut frame = body
- .layout(engine, locator, styles, region.into_regions())?
- .into_frame();
+ let mut frame = layout_frame(engine, body, locator, styles, region)?;
let Axes { x, y } = align.zip_map(frame.size(), FixedAlignment::position);
// Compute the transform.
diff --git a/crates/typst/src/lib.rs b/crates/typst/src/lib.rs
index cfcfd757..69c8dad8 100644
--- a/crates/typst/src/lib.rs
+++ b/crates/typst/src/lib.rs
@@ -26,7 +26,7 @@
//! [evaluate]: eval::eval
//! [module]: foundations::Module
//! [content]: foundations::Content
-//! [layouted]: foundations::Content::layout_document
+//! [layouted]: crate::layout::layout_document
//! [document]: model::Document
//! [frame]: layout::Frame
@@ -86,7 +86,7 @@ use crate::visualize::Color;
#[typst_macros::time]
pub fn compile(world: &dyn World) -> Warned<SourceResult<Document>> {
let mut sink = Sink::new();
- let output = compile_inner(world.track(), Traced::default().track(), &mut sink)
+ let output = compile_impl(world.track(), Traced::default().track(), &mut sink)
.map_err(deduplicate);
Warned { output, warnings: sink.warnings() }
}
@@ -97,12 +97,13 @@ pub fn compile(world: &dyn World) -> Warned<SourceResult<Document>> {
pub fn trace(world: &dyn World, span: Span) -> EcoVec<(Value, Option<Styles>)> {
let mut sink = Sink::new();
let traced = Traced::new(span);
- compile_inner(world.track(), traced.track(), &mut sink).ok();
+ compile_impl(world.track(), traced.track(), &mut sink).ok();
sink.values()
}
-/// Relayout until introspection converges.
-fn compile_inner(
+/// The internal implementation of `compile` with a bit lower-level interface
+/// that is also used by `trace`.
+fn compile_impl(
world: Tracked<dyn World + '_>,
traced: Tracked<Traced>,
sink: &mut Sink,
@@ -150,7 +151,7 @@ fn compile_inner(
};
// Layout!
- document = content.layout_document(&mut engine, styles)?;
+ document = crate::layout::layout_document(&mut engine, &content, styles)?;
document.introspector.rebuild(&document.pages);
iter += 1;
diff --git a/crates/typst/src/math/ctx.rs b/crates/typst/src/math/ctx.rs
index ac3fbca8..0eb97f9c 100644
--- a/crates/typst/src/math/ctx.rs
+++ b/crates/typst/src/math/ctx.rs
@@ -13,12 +13,11 @@ use crate::diag::SourceResult;
use crate::engine::Engine;
use crate::foundations::{Content, Packed, StyleChain};
use crate::introspection::{Locator, SplitLocator};
-use crate::layout::{Abs, Axes, BoxElem, Em, Frame, Regions, Size};
+use crate::layout::{layout_frame, Abs, Axes, BoxElem, Em, Frame, Region, Size};
use crate::math::{
scaled_font_size, styled_char, EquationElem, FrameFragment, GlyphFragment,
LayoutMath, MathFragment, MathRun, MathSize, THICK,
};
-use crate::model::ParElem;
use crate::realize::StyleVec;
use crate::syntax::{is_newline, Span};
use crate::text::{
@@ -51,7 +50,7 @@ pub struct MathContext<'a, 'b, 'v> {
// External.
pub engine: &'v mut Engine<'b>,
pub locator: SplitLocator<'v>,
- pub regions: Regions<'static>,
+ pub region: Region,
// Font-related.
pub font: &'a Font,
pub ttf: &'a ttf_parser::Face<'a>,
@@ -107,7 +106,7 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
Self {
engine,
locator: locator.split(),
- regions: Regions::one(base, Axes::splat(false)),
+ region: Region::new(base, Axes::splat(false)),
font,
ttf: font.ttf(),
table: math_table,
@@ -182,7 +181,7 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
self.engine,
self.locator.next(&boxed.span()),
styles.chain(&local),
- self.regions.base(),
+ self.region.size,
)
}
@@ -194,14 +193,13 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
) -> SourceResult<Frame> {
let local =
TextElem::set_size(TextSize(scaled_font_size(self, styles).into())).wrap();
- Ok(content
- .layout(
- self.engine,
- self.locator.next(&content.span()),
- styles.chain(&local),
- self.regions,
- )?
- .into_frame())
+ layout_frame(
+ self.engine,
+ content,
+ self.locator.next(&content.span()),
+ styles.chain(&local),
+ self.region,
+ )
}
/// Layout the given [`TextElem`] into a [`MathFragment`].
@@ -300,19 +298,17 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
// it will overflow. So emulate an `hbox` instead and allow the paragraph
// to extend as far as needed.
let spaced = text.graphemes(true).nth(1).is_some();
- let text = TextElem::packed(text).spanned(span);
- let par = ParElem::new(StyleVec::wrap(eco_vec![text]));
- let frame = Packed::new(par)
- .spanned(span)
- .layout(
- self.engine,
- self.locator.next(&span),
- styles,
- false,
- Size::splat(Abs::inf()),
- false,
- )?
- .into_frame();
+ let elem = TextElem::packed(text).spanned(span);
+ let frame = crate::layout::layout_inline(
+ self.engine,
+ &StyleVec::wrap(eco_vec![elem]),
+ self.locator.next(&span),
+ styles,
+ false,
+ Size::splat(Abs::inf()),
+ false,
+ )?
+ .into_frame();
Ok(FrameFragment::new(self, styles, frame)
.with_class(MathClass::Alphabetic)
diff --git a/crates/typst/src/math/equation.rs b/crates/typst/src/math/equation.rs
index 054b2823..df7cc021 100644
--- a/crates/typst/src/math/equation.rs
+++ b/crates/typst/src/math/equation.rs
@@ -10,9 +10,9 @@ use crate::foundations::{
};
use crate::introspection::{Count, Counter, CounterUpdate, Locatable, Locator};
use crate::layout::{
- Abs, AlignElem, Alignment, Axes, BlockElem, Em, FixedAlignment, Fragment, Frame,
- InlineElem, InlineItem, OuterHAlignment, Point, Regions, Size, SpecificAlignment,
- VAlignment,
+ layout_frame, Abs, AlignElem, Alignment, Axes, BlockElem, Em, FixedAlignment,
+ Fragment, Frame, InlineElem, InlineItem, OuterHAlignment, Point, Region, Regions,
+ Size, SpecificAlignment, VAlignment,
};
use crate::math::{
scaled_font_size, LayoutMath, MathContext, MathRunFrameBuilder, MathSize, MathVariant,
@@ -399,12 +399,11 @@ fn layout_equation_block(
return Ok(Fragment::frames(frames));
};
- let pod = Regions::one(regions.base(), Axes::splat(false));
- let number = Counter::of(EquationElem::elem())
+ let pod = Region::new(regions.base(), Axes::splat(false));
+ let counter = Counter::of(EquationElem::elem())
.display_at_loc(engine, elem.location().unwrap(), styles, numbering)?
- .spanned(span)
- .layout(engine, locator.next(&()), styles, pod)?
- .into_frame();
+ .spanned(span);
+ let number = layout_frame(engine, &counter, locator.next(&()), styles, pod)?;
static NUMBER_GUTTER: Em = Em::new(0.5);
let full_number_width = number.width() + NUMBER_GUTTER.resolve(styles);
diff --git a/crates/typst/src/math/matrix.rs b/crates/typst/src/math/matrix.rs
index 51449047..9f9b82bc 100644
--- a/crates/typst/src/math/matrix.rs
+++ b/crates/typst/src/math/matrix.rs
@@ -434,7 +434,7 @@ fn layout_vec_body(
row_gap: Rel<Abs>,
alternator: LeftRightAlternator,
) -> SourceResult<Frame> {
- let gap = row_gap.relative_to(ctx.regions.base().y);
+ let gap = row_gap.relative_to(ctx.region.size.y);
let denom_style = style_for_denominator(styles);
let mut flat = vec![];
@@ -464,7 +464,7 @@ fn layout_mat_body(
return Ok(Frame::soft(Size::zero()));
}
- let gap = gap.zip_map(ctx.regions.base(), Rel::relative_to);
+ let gap = gap.zip_map(ctx.region.size, Rel::relative_to);
let half_gap = gap * 0.5;
// We provide a default stroke thickness that scales
diff --git a/crates/typst/src/model/document.rs b/crates/typst/src/model/document.rs
index 71e9e796..d37f6c8f 100644
--- a/crates/typst/src/model/document.rs
+++ b/crates/typst/src/model/document.rs
@@ -3,12 +3,11 @@ use ecow::EcoString;
use crate::diag::{bail, HintedStrResult, SourceResult};
use crate::engine::Engine;
use crate::foundations::{
- cast, elem, Args, Array, Construct, Content, Datetime, Fields, Packed, Smart,
- StyleChain, Styles, Value,
+ cast, elem, Args, Array, Construct, Content, Datetime, Fields, Smart, StyleChain,
+ Styles, Value,
};
-use crate::introspection::{Introspector, Locator, ManualPageCounter};
-use crate::layout::{Page, PageElem};
-use crate::realize::StyleVec;
+use crate::introspection::Introspector;
+use crate::layout::Page;
/// The root element of a document and its metadata.
///
@@ -57,11 +56,6 @@ pub struct DocumentElem {
/// something other than `{auto}`.
#[ghost]
pub date: Smart<Option<Datetime>>,
-
- /// The page runs.
- #[internal]
- #[variadic]
- pub children: StyleVec,
}
impl Construct for DocumentElem {
@@ -70,48 +64,6 @@ impl Construct for DocumentElem {
}
}
-impl Packed<DocumentElem> {
- /// Layout this document.
- #[typst_macros::time(name = "document", span = self.span())]
- pub fn layout(
- &self,
- engine: &mut Engine,
- locator: Locator,
- styles: StyleChain,
- info: DocumentInfo,
- ) -> SourceResult<Document> {
- let children = self.children();
- let mut peekable = children.chain(&styles).peekable();
- let mut locator = locator.split();
-
- let iter = std::iter::from_fn(|| {
- let (child, styles) = peekable.next()?;
- let extend_to = peekable
- .peek()
- .and_then(|(next, _)| *next.to_packed::<PageElem>()?.clear_to()?);
- let locator = locator.next(&child.span());
- Some((child, styles, extend_to, locator))
- });
-
- let layouts =
- engine.parallelize(iter, |engine, (child, styles, extend_to, locator)| {
- if let Some(page) = child.to_packed::<PageElem>() {
- page.layout(engine, locator, styles, extend_to)
- } else {
- bail!(child.span(), "unexpected document child");
- }
- });
-
- let mut page_counter = ManualPageCounter::new();
- let mut pages = Vec::with_capacity(self.children().len());
- for result in layouts {
- pages.extend(result?.finalize(engine, &mut page_counter)?);
- }
-
- Ok(Document { pages, info, introspector: Introspector::default() })
- }
-}
-
/// A list of authors.
#[derive(Debug, Default, Clone, PartialEq, Hash)]
pub struct Author(Vec<EcoString>);
diff --git a/crates/typst/src/model/heading.rs b/crates/typst/src/model/heading.rs
index e065d3ac..6f126ff5 100644
--- a/crates/typst/src/model/heading.rs
+++ b/crates/typst/src/model/heading.rs
@@ -9,7 +9,9 @@ use crate::foundations::{
use crate::introspection::{
Count, Counter, CounterUpdate, Locatable, Locator, LocatorLink,
};
-use crate::layout::{Abs, Axes, BlockChild, BlockElem, Em, HElem, Length, Regions};
+use crate::layout::{
+ layout_frame, Abs, Axes, BlockChild, BlockElem, Em, HElem, Length, Region,
+};
use crate::model::{Numbering, Outlinable, ParElem, Refable, Supplement};
use crate::text::{FontWeight, LocalName, SpaceElem, TextElem, TextSize};
use crate::utils::NonZeroExt;
@@ -233,15 +235,14 @@ impl Show for Packed<HeadingElem> {
.spanned(span);
if hanging_indent.is_auto() {
- let pod = Regions::one(Axes::splat(Abs::inf()), Axes::splat(false));
+ let pod = Region::new(Axes::splat(Abs::inf()), Axes::splat(false));
// We don't have a locator for the numbering here, so we just
// use the measurement infrastructure for now.
let link = LocatorLink::measure(location);
- let size = numbering
- .layout(engine, Locator::link(&link), styles, pod)?
- .into_frame()
- .size();
+ let size =
+ layout_frame(engine, &numbering, Locator::link(&link), styles, pod)?
+ .size();
indent = size.x + SPACING_TO_NUMBERING.resolve(styles);
}
diff --git a/crates/typst/src/model/par.rs b/crates/typst/src/model/par.rs
index 2110995f..4d9f815e 100644
--- a/crates/typst/src/model/par.rs
+++ b/crates/typst/src/model/par.rs
@@ -3,11 +3,9 @@ use std::fmt::{self, Debug, Formatter};
use crate::diag::SourceResult;
use crate::engine::Engine;
use crate::foundations::{
- elem, Args, Cast, Construct, Content, NativeElement, Packed, Set, Smart, StyleChain,
- Unlabellable,
+ elem, Args, Cast, Construct, Content, NativeElement, Packed, Set, Smart, Unlabellable,
};
-use crate::introspection::Locator;
-use crate::layout::{Em, Fragment, Length, Size};
+use crate::layout::{Em, Length};
use crate::realize::StyleVec;
/// Arranges text, spacing and inline-level elements into a paragraph.
@@ -159,30 +157,6 @@ impl Construct for ParElem {
}
}
-impl Packed<ParElem> {
- /// Layout the paragraph into a collection of lines.
- #[typst_macros::time(name = "par", span = self.span())]
- pub fn layout(
- &self,
- engine: &mut Engine,
- locator: Locator,
- styles: StyleChain,
- consecutive: bool,
- region: Size,
- expand: bool,
- ) -> SourceResult<Fragment> {
- crate::layout::layout_inline(
- &self.children,
- engine,
- locator,
- styles,
- consecutive,
- region,
- expand,
- )
- }
-}
-
impl Debug for ParElem {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "Par ")?;
diff --git a/crates/typst/src/realize/mod.rs b/crates/typst/src/realize/mod.rs
index 721d4b4a..eb968605 100644
--- a/crates/typst/src/realize/mod.rs
+++ b/crates/typst/src/realize/mod.rs
@@ -23,7 +23,7 @@ use crate::engine::{Engine, Route};
use crate::foundations::{
Content, NativeElement, Packed, SequenceElem, Smart, StyleChain, StyledElem, Styles,
};
-use crate::introspection::{Locator, SplitLocator, TagElem};
+use crate::introspection::{SplitLocator, TagElem};
use crate::layout::{
AlignElem, BlockElem, BoxElem, ColbreakElem, FlowElem, FlushElem, HElem, InlineElem,
PageElem, PagebreakElem, Parity, PlaceElem, VElem,
@@ -36,16 +36,15 @@ use crate::model::{
use crate::syntax::Span;
use crate::text::{LinebreakElem, SmartQuoteElem, SpaceElem, TextElem};
-/// Realize into a `DocumentElem`, an element that is capable of root-level
-/// layout.
-#[typst_macros::time(name = "realize doc")]
-pub fn realize_doc<'a>(
- engine: &mut Engine,
- locator: Locator,
+/// Realize at the root-level.
+#[typst_macros::time(name = "realize root")]
+pub fn realize_root<'a>(
+ engine: &mut Engine<'a>,
+ locator: &mut SplitLocator<'a>,
arenas: &'a Arenas<'a>,
content: &'a Content,
styles: StyleChain<'a>,
-) -> SourceResult<(Packed<DocumentElem>, StyleChain<'a>, DocumentInfo)> {
+) -> SourceResult<(StyleVec, StyleChain<'a>, DocumentInfo)> {
let mut builder = Builder::new(engine, locator, arenas, true);
builder.accept(content, styles)?;
builder.interrupt_page(Some(styles), true)?;
@@ -55,8 +54,8 @@ pub fn realize_doc<'a>(
/// Realize into a `FlowElem`, an element that is capable of block-level layout.
#[typst_macros::time(name = "realize flow")]
pub fn realize_flow<'a>(
- engine: &mut Engine,
- locator: Locator,
+ engine: &mut Engine<'a>,
+ locator: &mut SplitLocator<'a>,
arenas: &'a Arenas<'a>,
content: &'a Content,
styles: StyleChain<'a>,
@@ -68,11 +67,11 @@ pub fn realize_flow<'a>(
}
/// Builds a document or a flow element from content.
-struct Builder<'a, 'v, 't> {
+struct Builder<'a, 'v> {
/// The engine.
- engine: &'v mut Engine<'t>,
+ engine: &'v mut Engine<'a>,
/// Assigns unique locations to elements.
- locator: SplitLocator<'v>,
+ locator: &'v mut SplitLocator<'a>,
/// Scratch arenas for building.
arenas: &'a Arenas<'a>,
/// The current document building state.
@@ -87,16 +86,16 @@ struct Builder<'a, 'v, 't> {
cites: CiteGroupBuilder<'a>,
}
-impl<'a, 'v, 't> Builder<'a, 'v, 't> {
+impl<'a, 'v> Builder<'a, 'v> {
fn new(
- engine: &'v mut Engine<'t>,
- locator: Locator<'v>,
+ engine: &'v mut Engine<'a>,
+ locator: &'v mut SplitLocator<'a>,
arenas: &'a Arenas<'a>,
top: bool,
) -> Self {
Self {
engine,
- locator: locator.split(),
+ locator,
arenas,
doc: top.then(DocBuilder::default),
flow: FlowBuilder::default(),
@@ -121,8 +120,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
// Styled elements and sequences can (at least currently) also have
// labels, so this needs to happen before they are handled.
- if let Some(realized) = process(self.engine, &mut self.locator, content, styles)?
- {
+ if let Some(realized) = process(self.engine, self.locator, content, styles)? {
self.engine.route.increase();
if !self.engine.route.within(Route::MAX_SHOW_RULE_DEPTH) {
bail!(
@@ -350,11 +348,11 @@ impl<'a> DocBuilder<'a> {
false
}
- /// Turns this builder into the resulting document, along with
+ /// Turns this builder into the resulting page runs, along with
/// its [style chain][StyleChain].
- fn finish(self) -> (Packed<DocumentElem>, StyleChain<'a>, DocumentInfo) {
- let (children, trunk, span) = self.pages.finish();
- (Packed::new(DocumentElem::new(children)).spanned(span), trunk, self.info)
+ fn finish(self) -> (StyleVec, StyleChain<'a>, DocumentInfo) {
+ let (children, trunk, _) = self.pages.finish();
+ (children, trunk, self.info)
}
}
diff --git a/crates/typst/src/visualize/pattern.rs b/crates/typst/src/visualize/pattern.rs
index 804c87df..daff5fa0 100644
--- a/crates/typst/src/visualize/pattern.rs
+++ b/crates/typst/src/visualize/pattern.rs
@@ -7,7 +7,7 @@ use crate::diag::{bail, SourceResult};
use crate::engine::Engine;
use crate::foundations::{func, repr, scope, ty, Content, Smart, StyleChain};
use crate::introspection::Locator;
-use crate::layout::{Abs, Axes, Frame, Length, Regions, Size};
+use crate::layout::{layout_frame, Abs, Axes, Frame, Length, Region, Size};
use crate::syntax::{Span, Spanned};
use crate::utils::{LazyHash, Numeric};
use crate::visualize::RelativeTo;
@@ -192,8 +192,8 @@ impl Pattern {
let library = world.library();
let locator = Locator::root();
let styles = StyleChain::new(&library.styles);
- let pod = Regions::one(region, Axes::splat(false));
- let mut frame = body.layout(engine, locator, styles, pod)?.into_frame();
+ let pod = Region::new(region, Axes::splat(false));
+ let mut frame = layout_frame(engine, &body, locator, styles, pod)?;
// Set the size of the frame if the size is enforced.
if let Smart::Custom(size) = size {
diff --git a/crates/typst/src/visualize/shape.rs b/crates/typst/src/visualize/shape.rs
index e8a68fe3..b2125bf5 100644
--- a/crates/typst/src/visualize/shape.rs
+++ b/crates/typst/src/visualize/shape.rs
@@ -7,8 +7,8 @@ use crate::foundations::{
};
use crate::introspection::Locator;
use crate::layout::{
- Abs, Axes, BlockElem, Corner, Corners, Frame, FrameItem, Length, Point, Ratio,
- Region, Regions, Rel, Sides, Size,
+ layout_frame, Abs, Axes, BlockElem, Corner, Corners, Frame, FrameItem, Length, Point,
+ Ratio, Region, Rel, Sides, Size,
};
use crate::syntax::Span;
use crate::utils::Get;
@@ -495,16 +495,14 @@ fn layout_shape(
}
// Layout the child.
- frame = child
- .layout(engine, locator.relayout(), styles, pod.into_regions())?
- .into_frame();
+ frame = layout_frame(engine, child, locator.relayout(), styles, pod)?;
// If the child is a square or circle, relayout with full expansion into
// square region to make sure the result is really quadratic.
if kind.is_quadratic() {
let length = frame.size().max_by_side().min(pod.size.min_by_side());
- let quad_pod = Regions::one(Size::splat(length), Axes::splat(true));
- frame = child.layout(engine, locator, styles, quad_pod)?.into_frame();
+ let quad_pod = Region::new(Size::splat(length), Axes::splat(true));
+ frame = layout_frame(engine, child, locator, styles, quad_pod)?;
}
// Apply the inset.