summaryrefslogtreecommitdiff
path: root/crates/typst-layout/src/flow
diff options
context:
space:
mode:
Diffstat (limited to 'crates/typst-layout/src/flow')
-rw-r--r--crates/typst-layout/src/flow/collect.rs85
-rw-r--r--crates/typst-layout/src/flow/compose.rs6
-rw-r--r--crates/typst-layout/src/flow/mod.rs45
3 files changed, 107 insertions, 29 deletions
diff --git a/crates/typst-layout/src/flow/collect.rs b/crates/typst-layout/src/flow/collect.rs
index 76d7b743..f2c7ebd1 100644
--- a/crates/typst-layout/src/flow/collect.rs
+++ b/crates/typst-layout/src/flow/collect.rs
@@ -20,13 +20,15 @@ use typst_library::model::ParElem;
use typst_library::routines::{Pair, Routines};
use typst_library::text::TextElem;
use typst_library::World;
+use typst_utils::SliceExt;
-use super::{layout_multi_block, layout_single_block};
+use super::{layout_multi_block, layout_single_block, FlowMode};
use crate::modifiers::layout_and_modify;
/// Collects all elements of the flow into prepared children. These are much
/// simpler to handle than the raw elements.
#[typst_macros::time]
+#[allow(clippy::too_many_arguments)]
pub fn collect<'a>(
engine: &mut Engine,
bump: &'a Bump,
@@ -34,6 +36,7 @@ pub fn collect<'a>(
locator: Locator<'a>,
base: Size,
expand: bool,
+ mode: FlowMode,
) -> SourceResult<Vec<Child<'a>>> {
Collector {
engine,
@@ -45,7 +48,7 @@ pub fn collect<'a>(
output: Vec::with_capacity(children.len()),
last_was_par: false,
}
- .run()
+ .run(mode)
}
/// State for collection.
@@ -62,7 +65,15 @@ struct Collector<'a, 'x, 'y> {
impl<'a> Collector<'a, '_, '_> {
/// Perform the collection.
- fn run(mut self) -> SourceResult<Vec<Child<'a>>> {
+ fn run(self, mode: FlowMode) -> SourceResult<Vec<Child<'a>>> {
+ match mode {
+ FlowMode::Root | FlowMode::Block => self.run_block(),
+ FlowMode::Inline => self.run_inline(),
+ }
+ }
+
+ /// Perform collection for block-level children.
+ fn run_block(mut self) -> SourceResult<Vec<Child<'a>>> {
for &(child, styles) in self.children {
if let Some(elem) = child.to_packed::<TagElem>() {
self.output.push(Child::Tag(&elem.tag));
@@ -95,6 +106,43 @@ impl<'a> Collector<'a, '_, '_> {
Ok(self.output)
}
+ /// Perform collection for inline-level children.
+ fn run_inline(mut self) -> SourceResult<Vec<Child<'a>>> {
+ // Extract leading and trailing tags.
+ let (start, end) = self.children.split_prefix_suffix(|(c, _)| c.is::<TagElem>());
+ let inner = &self.children[start..end];
+
+ // Compute the shared styles, ignoring tags.
+ let styles = StyleChain::trunk(inner.iter().map(|&(_, s)| s)).unwrap_or_default();
+
+ // Layout the lines.
+ let lines = crate::inline::layout_inline(
+ self.engine,
+ inner,
+ &mut self.locator,
+ styles,
+ self.base,
+ self.expand,
+ false,
+ false,
+ )?
+ .into_frames();
+
+ for (c, _) in &self.children[..start] {
+ let elem = c.to_packed::<TagElem>().unwrap();
+ self.output.push(Child::Tag(&elem.tag));
+ }
+
+ self.lines(lines, styles);
+
+ for (c, _) in &self.children[end..] {
+ let elem = c.to_packed::<TagElem>().unwrap();
+ self.output.push(Child::Tag(&elem.tag));
+ }
+
+ Ok(self.output)
+ }
+
/// Collect vertical spacing into a relative or fractional child.
fn v(&mut self, elem: &'a Packed<VElem>, styles: StyleChain<'a>) {
self.output.push(match elem.amount {
@@ -110,23 +158,33 @@ impl<'a> Collector<'a, '_, '_> {
elem: &'a Packed<ParElem>,
styles: StyleChain<'a>,
) -> SourceResult<()> {
- let align = AlignElem::alignment_in(styles).resolve(styles);
- let leading = ParElem::leading_in(styles);
- let spacing = ParElem::spacing_in(styles);
- let costs = TextElem::costs_in(styles);
-
- let lines = crate::layout_inline(
+ let lines = crate::inline::layout_par(
+ elem,
self.engine,
- &elem.children,
self.locator.next(&elem.span()),
styles,
- self.last_was_par,
self.base,
self.expand,
+ self.last_was_par,
)?
.into_frames();
+ let spacing = ParElem::spacing_in(styles);
+ self.output.push(Child::Rel(spacing.into(), 4));
+
+ self.lines(lines, styles);
+
self.output.push(Child::Rel(spacing.into(), 4));
+ self.last_was_par = true;
+
+ Ok(())
+ }
+
+ /// Collect laid-out lines.
+ fn lines(&mut self, lines: Vec<Frame>, styles: StyleChain<'a>) {
+ let align = AlignElem::alignment_in(styles).resolve(styles);
+ let leading = ParElem::leading_in(styles);
+ let costs = TextElem::costs_in(styles);
// Determine whether to prevent widow and orphans.
let len = lines.len();
@@ -166,11 +224,6 @@ impl<'a> Collector<'a, '_, '_> {
self.output
.push(Child::Line(self.boxed(LineChild { frame, align, need })));
}
-
- self.output.push(Child::Rel(spacing.into(), 4));
- self.last_was_par = true;
-
- Ok(())
}
/// Collect a block into a [`SingleChild`] or [`MultiChild`] depending on
diff --git a/crates/typst-layout/src/flow/compose.rs b/crates/typst-layout/src/flow/compose.rs
index 3cf66f9e..76af8f65 100644
--- a/crates/typst-layout/src/flow/compose.rs
+++ b/crates/typst-layout/src/flow/compose.rs
@@ -17,7 +17,9 @@ use typst_library::model::{
use typst_syntax::Span;
use typst_utils::{NonZeroExt, Numeric};
-use super::{distribute, Config, FlowResult, LineNumberConfig, PlacedChild, Stop, Work};
+use super::{
+ distribute, Config, FlowMode, FlowResult, LineNumberConfig, PlacedChild, Stop, Work,
+};
/// Composes the contents of a single page/region. A region can have multiple
/// columns/subregions.
@@ -356,7 +358,7 @@ impl<'a, 'b> Composer<'a, 'b, '_, '_> {
migratable: bool,
) -> FlowResult<()> {
// Footnotes are only supported at the root level.
- if !self.config.root {
+ if self.config.mode != FlowMode::Root {
return Ok(());
}
diff --git a/crates/typst-layout/src/flow/mod.rs b/crates/typst-layout/src/flow/mod.rs
index 2f0ec39a..2acbbcef 100644
--- a/crates/typst-layout/src/flow/mod.rs
+++ b/crates/typst-layout/src/flow/mod.rs
@@ -25,7 +25,7 @@ use typst_library::layout::{
Regions, Rel, Size,
};
use typst_library::model::{FootnoteElem, FootnoteEntry, LineNumberingScope, ParLine};
-use typst_library::routines::{Arenas, Pair, RealizationKind, Routines};
+use typst_library::routines::{Arenas, FragmentKind, Pair, RealizationKind, Routines};
use typst_library::text::TextElem;
use typst_library::World;
use typst_utils::{NonZeroExt, Numeric};
@@ -140,9 +140,10 @@ fn layout_fragment_impl(
engine.route.check_layout_depth().at(content.span())?;
+ let mut kind = FragmentKind::Block;
let arenas = Arenas::default();
let children = (engine.routines.realize)(
- RealizationKind::LayoutFragment,
+ RealizationKind::LayoutFragment(&mut kind),
&mut engine,
&mut locator,
&arenas,
@@ -158,25 +159,46 @@ fn layout_fragment_impl(
regions,
columns,
column_gutter,
- false,
+ kind.into(),
)
}
+/// The mode a flow can be laid out in.
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum FlowMode {
+ /// A root flow with block-level elements. Like `FlowMode::Block`, but can
+ /// additionally host footnotes and line numbers.
+ Root,
+ /// A flow whose children are block-level elements.
+ Block,
+ /// A flow whose children are inline-level elements.
+ Inline,
+}
+
+impl From<FragmentKind> for FlowMode {
+ fn from(value: FragmentKind) -> Self {
+ match value {
+ FragmentKind::Inline => Self::Inline,
+ FragmentKind::Block => Self::Block,
+ }
+ }
+}
+
/// Lays out realized content into regions, potentially with columns.
#[allow(clippy::too_many_arguments)]
-pub(crate) fn layout_flow(
+pub fn layout_flow<'a>(
engine: &mut Engine,
- children: &[Pair],
- locator: &mut SplitLocator,
- shared: StyleChain,
+ children: &[Pair<'a>],
+ locator: &mut SplitLocator<'a>,
+ shared: StyleChain<'a>,
mut regions: Regions,
columns: NonZeroUsize,
column_gutter: Rel<Abs>,
- root: bool,
+ mode: FlowMode,
) -> SourceResult<Fragment> {
// Prepare configuration that is shared across the whole flow.
let config = Config {
- root,
+ mode,
shared,
columns: {
let mut count = columns.get();
@@ -195,7 +217,7 @@ pub(crate) fn layout_flow(
gap: FootnoteEntry::gap_in(shared),
expand: regions.expand.x,
},
- line_numbers: root.then(|| LineNumberConfig {
+ line_numbers: (mode == FlowMode::Root).then(|| LineNumberConfig {
scope: ParLine::numbering_scope_in(shared),
default_clearance: {
let width = if PageElem::flipped_in(shared) {
@@ -225,6 +247,7 @@ pub(crate) fn layout_flow(
locator.next(&()),
Size::new(config.columns.width, regions.full),
regions.expand.x,
+ mode,
)?;
let mut work = Work::new(&children);
@@ -318,7 +341,7 @@ impl<'a, 'b> Work<'a, 'b> {
struct Config<'x> {
/// Whether this is the root flow, which can host footnotes and line
/// numbers.
- root: bool,
+ mode: FlowMode,
/// The styles shared by the whole flow. This is used for footnotes and line
/// numbers.
shared: StyleChain<'x>,