summaryrefslogtreecommitdiff
path: root/crates/typst-library/src/layout
diff options
context:
space:
mode:
authorSébastien d'Herbais de Thun <sebastien.d.herbais@gmail.com>2023-11-06 21:37:50 +0100
committerGitHub <noreply@github.com>2023-11-06 21:37:50 +0100
commitc0f6d2004afebfa9412ba0c2d598ef8287197c42 (patch)
tree4bb034ca671e7d1982a306f5aecfc4f78a01841d /crates/typst-library/src/layout
parent8fd546760c7c425398f0114997c8085a481d8d2a (diff)
Content rework 2 - Electric Boogaloo (#2504)
Diffstat (limited to 'crates/typst-library/src/layout')
-rw-r--r--crates/typst-library/src/layout/align.rs5
-rw-r--r--crates/typst-library/src/layout/enum.rs8
-rw-r--r--crates/typst-library/src/layout/flow.rs10
-rw-r--r--crates/typst-library/src/layout/grid.rs24
-rw-r--r--crates/typst-library/src/layout/hide.rs4
-rw-r--r--crates/typst-library/src/layout/list.rs8
-rw-r--r--crates/typst-library/src/layout/mod.rs49
-rw-r--r--crates/typst-library/src/layout/page.rs41
-rw-r--r--crates/typst-library/src/layout/par.rs62
-rw-r--r--crates/typst-library/src/layout/place.rs5
-rw-r--r--crates/typst-library/src/layout/spacing.rs6
-rw-r--r--crates/typst-library/src/layout/stack.rs6
-rw-r--r--crates/typst-library/src/layout/table.rs32
-rw-r--r--crates/typst-library/src/layout/terms.rs11
14 files changed, 160 insertions, 111 deletions
diff --git a/crates/typst-library/src/layout/align.rs b/crates/typst-library/src/layout/align.rs
index f080f677..9c18266d 100644
--- a/crates/typst-library/src/layout/align.rs
+++ b/crates/typst-library/src/layout/align.rs
@@ -38,6 +38,9 @@ pub struct AlignElem {
impl Show for AlignElem {
#[tracing::instrument(name = "AlignElem::show", skip_all)]
fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
- Ok(self.body().styled(Self::set_alignment(self.alignment(styles))))
+ Ok(self
+ .body()
+ .clone()
+ .styled(Self::set_alignment(self.alignment(styles))))
}
}
diff --git a/crates/typst-library/src/layout/enum.rs b/crates/typst-library/src/layout/enum.rs
index 436aacb9..8c491dca 100644
--- a/crates/typst-library/src/layout/enum.rs
+++ b/crates/typst-library/src/layout/enum.rs
@@ -107,6 +107,7 @@ pub struct EnumElem {
/// + Numbering!
/// ```
#[default(Numbering::Pattern(NumberingPattern::from_str("1.").unwrap()))]
+ #[borrowed]
pub numbering: Numbering,
/// Which number to start the enumeration with.
@@ -215,7 +216,7 @@ impl Layout for EnumElem {
ParElem::leading_in(styles).into()
} else {
self.spacing(styles)
- .unwrap_or_else(|| BlockElem::below_in(styles).amount())
+ .unwrap_or_else(|| *BlockElem::below_in(styles).amount())
};
let mut cells = vec![];
@@ -238,7 +239,7 @@ impl Layout for EnumElem {
parents.pop();
content
} else {
- match &numbering {
+ match numbering {
Numbering::Pattern(pattern) => {
TextElem::packed(pattern.apply_kth(parents.len(), number))
}
@@ -254,7 +255,7 @@ impl Layout for EnumElem {
cells.push(Content::empty());
cells.push(resolved);
cells.push(Content::empty());
- cells.push(item.body().styled(Self::set_parents(Parent(number))));
+ cells.push(item.body().clone().styled(Self::set_parents(Parent(number))));
number = number.saturating_add(1);
}
@@ -301,6 +302,7 @@ cast! {
v: Content => v.to::<Self>().cloned().unwrap_or_else(|| Self::new(v.clone())),
}
+#[derive(Debug, Clone, Copy, PartialEq, Hash)]
struct Parent(usize);
cast! {
diff --git a/crates/typst-library/src/layout/flow.rs b/crates/typst-library/src/layout/flow.rs
index 1feee4b8..5c795c81 100644
--- a/crates/typst-library/src/layout/flow.rs
+++ b/crates/typst-library/src/layout/flow.rs
@@ -1,5 +1,7 @@
use std::mem;
+use comemo::Prehashed;
+
use super::{
AlignElem, BlockElem, ColbreakElem, ColumnsElem, ParElem, PlaceElem, Spacing, VElem,
};
@@ -18,7 +20,7 @@ use crate::visualize::{
pub struct FlowElem {
/// The children that will be arranges into a flow.
#[variadic]
- pub children: Vec<Content>,
+ pub children: Vec<Prehashed<Content>>,
}
impl Layout for FlowElem {
@@ -37,7 +39,7 @@ impl Layout for FlowElem {
}
let mut layouter = FlowLayouter::new(regions, styles);
- for mut child in &self.children() {
+ for mut child in self.children().iter().map(|c| &**c) {
let outer = styles;
let mut styles = styles;
if let Some((elem, map)) = child.to_styled() {
@@ -199,7 +201,7 @@ impl<'a> FlowLayouter<'a> {
rel.resolve(styles).relative_to(self.initial.y),
v.weakness(styles) > 0,
),
- Spacing::Fr(fr) => FlowItem::Fractional(fr),
+ Spacing::Fr(fr) => FlowItem::Fractional(*fr),
},
)
}
@@ -701,7 +703,7 @@ fn find_footnotes(notes: &mut Vec<FootnoteElem>, frame: &Frame) {
match item {
FrameItem::Group(group) => find_footnotes(notes, &group.frame),
FrameItem::Meta(Meta::Elem(content), _)
- if !notes.iter().any(|note| note.0.location() == content.location()) =>
+ if !notes.iter().any(|note| note.location() == content.location()) =>
{
let Some(footnote) = content.to::<FootnoteElem>() else { continue };
notes.push(footnote.clone());
diff --git a/crates/typst-library/src/layout/grid.rs b/crates/typst-library/src/layout/grid.rs
index 8d015782..b29adeb9 100644
--- a/crates/typst-library/src/layout/grid.rs
+++ b/crates/typst-library/src/layout/grid.rs
@@ -1,3 +1,5 @@
+use smallvec::{smallvec, SmallVec};
+
use crate::prelude::*;
use crate::text::TextElem;
@@ -66,12 +68,14 @@ pub struct GridElem {
/// with that many `{auto}`-sized columns. Note that opposed to rows and
/// gutters, providing a single track size will only ever create a single
/// column.
+ #[borrowed]
pub columns: TrackSizings,
/// The row sizes.
///
/// If there are more cells than fit the defined rows, the last row is
/// repeated until there are no more cells.
+ #[borrowed]
pub rows: TrackSizings,
/// The gaps between rows & columns.
@@ -85,10 +89,12 @@ pub struct GridElem {
let gutter = args.named("gutter")?;
args.named("column-gutter")?.or_else(|| gutter.clone())
)]
+ #[borrowed]
pub column_gutter: TrackSizings,
/// The gaps between rows. Takes precedence over `gutter`.
#[parse(args.named("row-gutter")?.or_else(|| gutter.clone()))]
+ #[borrowed]
pub row_gutter: TrackSizings,
/// The contents of the grid cells.
@@ -106,12 +112,16 @@ impl Layout for GridElem {
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
+ let columns = self.columns(styles);
+ let rows = self.rows(styles);
+ let column_gutter = self.column_gutter(styles);
+ let row_gutter = self.row_gutter(styles);
+
// Prepare grid layout by unifying content and gutter tracks.
- let cells = self.children();
let layouter = GridLayouter::new(
- Axes::new(&self.columns(styles).0, &self.rows(styles).0),
- Axes::new(&self.column_gutter(styles).0, &self.row_gutter(styles).0),
- &cells,
+ Axes::new(&columns.0, &rows.0),
+ Axes::new(&column_gutter.0, &row_gutter.0),
+ &self.children,
regions,
styles,
self.span(),
@@ -124,13 +134,13 @@ impl Layout for GridElem {
/// Track sizing definitions.
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
-pub struct TrackSizings(pub Vec<Sizing>);
+pub struct TrackSizings(pub SmallVec<[Sizing; 4]>);
cast! {
TrackSizings,
self => self.0.into_value(),
- sizing: Sizing => Self(vec![sizing]),
- count: NonZeroUsize => Self(vec![Sizing::Auto; count.get()]),
+ sizing: Sizing => Self(smallvec![sizing]),
+ count: NonZeroUsize => Self(smallvec![Sizing::Auto; count.get()]),
values: Array => Self(values.into_iter().map(Value::cast).collect::<StrResult<_>>()?),
}
diff --git a/crates/typst-library/src/layout/hide.rs b/crates/typst-library/src/layout/hide.rs
index 7f17a7d7..af3d0631 100644
--- a/crates/typst-library/src/layout/hide.rs
+++ b/crates/typst-library/src/layout/hide.rs
@@ -1,3 +1,5 @@
+use smallvec::smallvec;
+
use crate::prelude::*;
/// Hides content without affecting layout.
@@ -22,6 +24,6 @@ pub struct HideElem {
impl Show for HideElem {
#[tracing::instrument(name = "HideElem::show", skip(self))]
fn show(&self, _: &mut Vt, _: StyleChain) -> SourceResult<Content> {
- Ok(self.body().styled(MetaElem::set_data(vec![Meta::Hide])))
+ Ok(self.body().clone().styled(MetaElem::set_data(smallvec![Meta::Hide])))
}
}
diff --git a/crates/typst-library/src/layout/list.rs b/crates/typst-library/src/layout/list.rs
index 34e0e6fb..18c89a24 100644
--- a/crates/typst-library/src/layout/list.rs
+++ b/crates/typst-library/src/layout/list.rs
@@ -77,6 +77,7 @@ pub struct ListElem {
/// - Items
/// - Items
/// ```
+ #[borrowed]
#[default(ListMarker::Content(vec![TextElem::packed('•')]))]
pub marker: ListMarker,
@@ -133,7 +134,7 @@ impl Layout for ListElem {
ParElem::leading_in(styles).into()
} else {
self.spacing(styles)
- .unwrap_or_else(|| BlockElem::below_in(styles).amount())
+ .unwrap_or_else(|| *BlockElem::below_in(styles).amount())
};
let depth = self.depth(styles);
@@ -148,7 +149,7 @@ impl Layout for ListElem {
cells.push(Content::empty());
cells.push(marker.clone());
cells.push(Content::empty());
- cells.push(item.body().styled(Self::set_depth(Depth)));
+ cells.push(item.body().clone().styled(Self::set_depth(Depth)));
}
let layouter = GridLayouter::new(
@@ -183,7 +184,7 @@ cast! {
}
/// A list's marker.
-#[derive(Debug, Clone, Hash)]
+#[derive(Debug, Clone, PartialEq, Hash)]
pub enum ListMarker {
Content(Vec<Content>),
Func(Func),
@@ -221,6 +222,7 @@ cast! {
v: Func => Self::Func(v),
}
+#[derive(Debug, Clone, Copy, PartialEq, Hash)]
struct Depth;
cast! {
diff --git a/crates/typst-library/src/layout/mod.rs b/crates/typst-library/src/layout/mod.rs
index 1c7c2610..d31f6841 100644
--- a/crates/typst-library/src/layout/mod.rs
+++ b/crates/typst-library/src/layout/mod.rs
@@ -46,6 +46,7 @@ pub use self::table::*;
pub use self::terms::*;
pub use self::transform::*;
+use std::borrow::Cow;
use std::mem;
use typed_arena::Arena;
@@ -242,16 +243,16 @@ fn realize_root<'a>(
scratch: &'a Scratch<'a>,
content: &'a Content,
styles: StyleChain<'a>,
-) -> SourceResult<(Content, StyleChain<'a>)> {
+) -> SourceResult<(Cow<'a, Content>, StyleChain<'a>)> {
if content.can::<dyn LayoutRoot>() && !applicable(content, styles) {
- return Ok((content.clone(), styles));
+ return Ok((Cow::Borrowed(content), styles));
}
let mut builder = Builder::new(vt, scratch, true);
builder.accept(content, styles)?;
builder.interrupt_page(Some(styles), true)?;
let (pages, shared) = builder.doc.unwrap().pages.finish();
- Ok((DocumentElem::new(pages.to_vec()).pack(), shared))
+ Ok((Cow::Owned(DocumentElem::new(pages.to_vec()).pack()), shared))
}
/// Realize into an element that is capable of block-level layout.
@@ -261,7 +262,7 @@ fn realize_block<'a>(
scratch: &'a Scratch<'a>,
content: &'a Content,
styles: StyleChain<'a>,
-) -> SourceResult<(Content, StyleChain<'a>)> {
+) -> SourceResult<(Cow<'a, Content>, StyleChain<'a>)> {
// These elements implement `Layout` but still require a flow for
// proper layout.
if content.can::<dyn Layout>()
@@ -277,14 +278,14 @@ fn realize_block<'a>(
&& !content.is::<PlaceElem>()
&& !applicable(content, styles)
{
- return Ok((content.clone(), styles));
+ return Ok((Cow::Borrowed(content), styles));
}
let mut builder = Builder::new(vt, scratch, false);
builder.accept(content, styles)?;
builder.interrupt_par()?;
let (children, shared) = builder.flow.0.finish();
- Ok((FlowElem::new(children.to_vec()).pack(), shared))
+ Ok((Cow::Owned(FlowElem::new(children.to_vec()).pack()), shared))
}
/// Builds a document or a flow element from content.
@@ -509,7 +510,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
/// Accepts pagebreaks and pages.
struct DocBuilder<'a> {
/// The page runs built so far.
- pages: StyleVecBuilder<'a, Content>,
+ pages: StyleVecBuilder<'a, Cow<'a, Content>>,
/// Whether to keep a following page even if it is empty.
keep_next: bool,
/// Whether the next page should be cleared to an even or odd number.
@@ -517,7 +518,7 @@ struct DocBuilder<'a> {
}
impl<'a> DocBuilder<'a> {
- fn accept(&mut self, content: &Content, styles: StyleChain<'a>) -> bool {
+ fn accept(&mut self, content: &'a Content, styles: StyleChain<'a>) -> bool {
if let Some(pagebreak) = content.to::<PagebreakElem>() {
self.keep_next = !pagebreak.weak(styles);
self.clear_next = pagebreak.to(styles);
@@ -528,9 +529,9 @@ impl<'a> DocBuilder<'a> {
let elem = if let Some(clear_to) = self.clear_next.take() {
let mut page = page.clone();
page.push_clear_to(Some(clear_to));
- page.pack()
+ Cow::Owned(page.pack())
} else {
- content.clone()
+ Cow::Borrowed(content)
};
self.pages.push(elem, styles);
@@ -571,7 +572,7 @@ impl<'a> FlowBuilder<'a> {
|| content.is::<MetaElem>()
|| content.is::<PlaceElem>()
{
- self.0.push(content.clone(), styles);
+ self.0.push(Cow::Borrowed(content), styles);
return true;
}
@@ -589,7 +590,7 @@ impl<'a> FlowBuilder<'a> {
if !last_was_parbreak && is_tight_list {
let leading = ParElem::leading_in(styles);
let spacing = VElem::list_attach(leading.into());
- self.0.push(spacing.pack(), styles);
+ self.0.push(Cow::Owned(spacing.pack()), styles);
}
let (above, below) = if let Some(block) = content.to::<BlockElem>() {
@@ -598,9 +599,9 @@ impl<'a> FlowBuilder<'a> {
(BlockElem::above_in(styles), BlockElem::below_in(styles))
};
- self.0.push(above.pack(), styles);
- self.0.push(content.clone(), styles);
- self.0.push(below.pack(), styles);
+ self.0.push(Cow::Owned(above.pack()), styles);
+ self.0.push(Cow::Borrowed(content), styles);
+ self.0.push(Cow::Owned(below.pack()), styles);
return true;
}
@@ -616,7 +617,7 @@ impl<'a> ParBuilder<'a> {
fn accept(&mut self, content: &'a Content, styles: StyleChain<'a>) -> bool {
if content.is::<MetaElem>() {
if self.0.has_strong_elements(false) {
- self.0.push(content.clone(), styles);
+ self.0.push(Cow::Borrowed(content), styles);
return true;
}
} else if content.is::<SpaceElem>()
@@ -627,7 +628,7 @@ impl<'a> ParBuilder<'a> {
|| content.to::<EquationElem>().map_or(false, |elem| !elem.block(styles))
|| content.is::<BoxElem>()
{
- self.0.push(content.clone(), styles);
+ self.0.push(Cow::Borrowed(content), styles);
return true;
}
@@ -643,7 +644,7 @@ impl<'a> ParBuilder<'a> {
/// Accepts list / enum items, spaces, paragraph breaks.
struct ListBuilder<'a> {
/// The list items collected so far.
- items: StyleVecBuilder<'a, Content>,
+ items: StyleVecBuilder<'a, Cow<'a, Content>>,
/// Whether the list contains no paragraph breaks.
tight: bool,
/// Trailing content for which it is unclear whether it is part of the list.
@@ -668,7 +669,7 @@ impl<'a> ListBuilder<'a> {
.next()
.map_or(true, |first| first.func() == content.func())
{
- self.items.push(content.clone(), styles);
+ self.items.push(Cow::Borrowed(content), styles);
self.tight &= self.staged.drain(..).all(|(t, _)| !t.is::<ParbreakElem>());
return true;
}
@@ -685,7 +686,8 @@ impl<'a> ListBuilder<'a> {
.iter()
.map(|(item, local)| {
let item = item.to::<ListItem>().unwrap();
- item.clone().with_body(item.body().styled_with_map(local.clone()))
+ item.clone()
+ .with_body(item.body().clone().styled_with_map(local.clone()))
})
.collect::<Vec<_>>(),
)
@@ -697,7 +699,8 @@ impl<'a> ListBuilder<'a> {
.iter()
.map(|(item, local)| {
let item = item.to::<EnumItem>().unwrap();
- item.clone().with_body(item.body().styled_with_map(local.clone()))
+ item.clone()
+ .with_body(item.body().clone().styled_with_map(local.clone()))
})
.collect::<Vec<_>>(),
)
@@ -710,9 +713,9 @@ impl<'a> ListBuilder<'a> {
.map(|(item, local)| {
let item = item.to::<TermItem>().unwrap();
item.clone()
- .with_term(item.term().styled_with_map(local.clone()))
+ .with_term(item.term().clone().styled_with_map(local.clone()))
.with_description(
- item.description().styled_with_map(local.clone()),
+ item.description().clone().styled_with_map(local.clone()),
)
})
.collect::<Vec<_>>(),
diff --git a/crates/typst-library/src/layout/page.rs b/crates/typst-library/src/layout/page.rs
index 495b390d..5f8f90c1 100644
--- a/crates/typst-library/src/layout/page.rs
+++ b/crates/typst-library/src/layout/page.rs
@@ -1,3 +1,4 @@
+use std::borrow::Cow;
use std::ptr;
use std::str::FromStr;
@@ -172,6 +173,7 @@ pub struct PageElem {
/// #set text(fill: rgb("fdfdfd"))
/// *Dark mode enabled.*
/// ```
+ #[borrowed]
pub fill: Option<Paint>,
/// How to [number]($numbering) the pages.
@@ -188,6 +190,7 @@ pub struct PageElem {
///
/// #lorem(48)
/// ```
+ #[borrowed]
pub numbering: Option<Numbering>,
/// The alignment of the page numbering.
@@ -233,6 +236,7 @@ pub struct PageElem {
///
/// #lorem(19)
/// ```
+ #[borrowed]
pub header: Option<Content>,
/// The amount the header is raised into the top margin.
@@ -263,6 +267,7 @@ pub struct PageElem {
///
/// #lorem(48)
/// ```
+ #[borrowed]
pub footer: Option<Content>,
/// The amount the footer is lowered into the bottom margin.
@@ -286,6 +291,7 @@ pub struct PageElem {
/// In the year 2023, we plan to take
/// over the world (of typesetting).
/// ```
+ #[borrowed]
pub background: Option<Content>,
/// Content in the page's foreground.
@@ -299,6 +305,7 @@ pub struct PageElem {
/// "Weak Reject" because they did
/// not understand our approach...
/// ```
+ #[borrowed]
pub foreground: Option<Content>,
/// The contents of the page(s).
@@ -364,7 +371,7 @@ impl PageElem {
});
// Realize columns.
- let mut child = self.body();
+ let mut child = self.body().clone();
let columns = self.columns(styles);
if columns.get() > 1 {
child = ColumnsElem::new(child).with_count(columns).pack();
@@ -388,25 +395,25 @@ impl PageElem {
}
let fill = self.fill(styles);
- let foreground = self.foreground(styles);
- let background = self.background(styles);
+ let foreground = Cow::Borrowed(self.foreground(styles));
+ let background = Cow::Borrowed(self.background(styles));
let header_ascent = self.header_ascent(styles);
let footer_descent = self.footer_descent(styles);
let numbering = self.numbering(styles);
let numbering_meta = Meta::PageNumbering(numbering.clone().into_value());
let number_align = self.number_align(styles);
- let mut header = self.header(styles);
- let mut footer = self.footer(styles);
+ let mut header = Cow::Borrowed(self.header(styles));
+ let mut footer = Cow::Borrowed(self.footer(styles));
// Construct the numbering (for header or footer).
- let numbering_marginal = numbering.clone().map(|numbering| {
- let both = match &numbering {
+ let numbering_marginal = Cow::Owned(numbering.as_ref().map(|numbering| {
+ let both = match numbering {
Numbering::Pattern(pattern) => pattern.pieces() >= 2,
Numbering::Func(_) => true,
};
let mut counter =
- Counter::new(CounterKey::Page).display(Some(numbering), both);
+ Counter::new(CounterKey::Page).display(Some(numbering.clone()), both);
// We interpret the Y alignment as selecting header or footer
// and then ignore it for aligning the actual number.
@@ -415,12 +422,12 @@ impl PageElem {
}
counter
- });
+ }));
if matches!(number_align.y(), Some(VAlign::Top)) {
- header = header.or(numbering_marginal);
+ header = if header.is_some() { header } else { numbering_marginal };
} else {
- footer = footer.or(numbering_marginal);
+ footer = if footer.is_some() { footer } else { numbering_marginal };
}
// Post-process pages.
@@ -455,7 +462,7 @@ impl PageElem {
] {
tracing::info!("Layouting {name}");
- let Some(content) = marginal else { continue };
+ let Some(content) = &**marginal else { continue };
let (pos, area, align);
if ptr::eq(marginal, &header) {
@@ -488,14 +495,14 @@ impl PageElem {
}
}
- if let Some(fill) = &fill {
+ if let Some(fill) = fill {
frame.fill(fill.clone());
}
page_counter.visit(vt, frame)?;
// Add a PDF page label if there is a numbering.
- if let Some(num) = &numbering {
+ if let Some(num) = numbering {
if let Some(page_label) = num.apply_pdf(page_counter.logical()) {
frame.push_positionless_meta(Meta::PdfPageLabel(page_label));
}
@@ -657,10 +664,10 @@ pub enum Marginal {
impl Marginal {
/// Resolve the marginal based on the page number.
- pub fn resolve(&self, vt: &mut Vt, page: usize) -> SourceResult<Content> {
+ pub fn resolve(&self, vt: &mut Vt, page: usize) -> SourceResult<Cow<'_, Content>> {
Ok(match self {
- Self::Content(content) => content.clone(),
- Self::Func(func) => func.call_vt(vt, [page])?.display(),
+ Self::Content(content) => Cow::Borrowed(content),
+ Self::Func(func) => Cow::Owned(func.call_vt(vt, [page])?.display()),
})
}
}
diff --git a/crates/typst-library/src/layout/par.rs b/crates/typst-library/src/layout/par.rs
index 10f78ef2..b3d5fb3e 100644
--- a/crates/typst-library/src/layout/par.rs
+++ b/crates/typst-library/src/layout/par.rs
@@ -1,3 +1,4 @@
+use comemo::Prehashed;
use typst::eval::Tracer;
use typst::model::DelayedErrors;
use unicode_bidi::{BidiInfo, Level as BidiLevel};
@@ -104,7 +105,7 @@ pub struct ParElem {
/// The paragraph's children.
#[internal]
#[variadic]
- pub children: Vec<Content>,
+ pub children: Vec<Prehashed<Content>>,
}
impl Construct for ParElem {
@@ -158,12 +159,12 @@ impl ParElem {
let children = par.children();
// Collect all text into one string for BiDi analysis.
- let (text, segments, spans) = collect(&children, &styles, consecutive)?;
+ let (text, segments, spans) = collect(children, &styles, consecutive)?;
// Perform BiDi analysis and then prepare paragraph layout by building a
// representation on which we can do line breaking without layouting
// each and every line from scratch.
- let p = prepare(&mut vt, &children, &text, segments, spans, styles, region)?;
+ let p = prepare(&mut vt, children, &text, segments, spans, styles, region)?;
// Break the paragraph into lines.
let lines = linebreak(&vt, &p, region.x - p.hang);
@@ -246,8 +247,6 @@ pub(crate) struct Preparation<'a> {
pub items: Vec<Item<'a>>,
/// The span mapper.
pub spans: SpanMapper,
- /// The styles shared by all children.
- pub styles: StyleChain<'a>,
/// Whether to hyphenate if it's the same for all children.
pub hyphenate: Option<bool>,
/// The text language if it's the same for all children.
@@ -258,8 +257,16 @@ pub(crate) struct Preparation<'a> {
pub justify: bool,
/// The paragraph's hanging indent.
pub hang: Abs,
- /// The CJK-latin spacing.
+ /// Whether to add spacing between CJK and Latin characters.
pub cjk_latin_spacing: bool,
+ /// Whether font fallback is enabled for this paragraph.
+ pub fallback: bool,
+ /// The leading of the paragraph.
+ pub leading: Abs,
+ /// How to determine line breaks.
+ pub linebreaks: Smart<Linebreaks>,
+ /// The text size.
+ pub size: Abs,
}
impl<'a> Preparation<'a> {
@@ -525,15 +532,15 @@ impl<'a> Line<'a> {
/// string-level preprocessing like case transformations.
#[allow(clippy::type_complexity)]
fn collect<'a>(
- children: &'a [Content],
+ children: &'a [Prehashed<Content>],
styles: &'a StyleChain<'a>,
consecutive: bool,
) -> SourceResult<(String, Vec<(Segment<'a>, StyleChain<'a>)>, SpanMapper)> {
let mut full = String::new();
let mut quoter = Quoter::new();
- let mut segments = vec![];
+ let mut segments = Vec::with_capacity(2 + children.len());
let mut spans = SpanMapper::new();
- let mut iter = children.iter().peekable();
+ let mut iter = children.iter().map(|c| &**c).peekable();
let first_line_indent = ParElem::first_line_indent_in(*styles);
if !first_line_indent.is_zero()
@@ -565,9 +572,9 @@ fn collect<'a>(
} else if let Some(elem) = child.to::<TextElem>() {
let prev = full.len();
if let Some(case) = TextElem::case_in(styles) {
- full.push_str(&case.apply(&elem.text()));
+ full.push_str(&case.apply(elem.text()));
} else {
- full.push_str(&elem.text());
+ full.push_str(elem.text());
}
Segment::Text(full.len() - prev)
} else if let Some(elem) = child.to::<HElem>() {
@@ -576,7 +583,7 @@ fn collect<'a>(
}
full.push(SPACING_REPLACE);
- Segment::Spacing(elem.amount())
+ Segment::Spacing(*elem.amount())
} else if let Some(elem) = child.to::<LinebreakElem>() {
let c = if elem.justify(styles) { '\u{2028}' } else { '\n' };
full.push(c);
@@ -588,7 +595,7 @@ fn collect<'a>(
let lang = TextElem::lang_in(styles);
let region = TextElem::region_in(styles);
let quotes = Quotes::new(
- &quotes,
+ quotes,
lang,
region,
SmartquoteElem::alternative_in(styles),
@@ -656,7 +663,7 @@ fn collect<'a>(
/// contained inline-level content.
fn prepare<'a>(
vt: &mut Vt,
- children: &'a [Content],
+ children: &'a [Prehashed<Content>],
text: &'a str,
segments: Vec<(Segment<'a>, StyleChain<'a>)>,
spans: SpanMapper,
@@ -674,7 +681,7 @@ fn prepare<'a>(
);
let mut cursor = 0;
- let mut items = vec![];
+ let mut items = Vec::with_capacity(segments.len());
// Shape / layout the children and collect them into items.
for (segment, styles) in segments {
@@ -727,13 +734,16 @@ fn prepare<'a>(
bidi,
items,
spans,
- styles,
hyphenate: shared_get(styles, children, TextElem::hyphenate_in),
lang: shared_get(styles, children, TextElem::lang_in),
align: AlignElem::alignment_in(styles).resolve(styles).x,
justify: ParElem::justify_in(styles),
hang: ParElem::hanging_indent_in(styles),
cjk_latin_spacing,
+ fallback: TextElem::fallback_in(styles),
+ leading: ParElem::leading_in(styles),
+ linebreaks: ParElem::linebreaks_in(styles),
+ size: TextElem::size_in(styles),
})
}
@@ -852,7 +862,7 @@ fn is_compatible(a: Script, b: Script) -> bool {
/// paragraph.
fn shared_get<T: PartialEq>(
styles: StyleChain<'_>,
- children: &[Content],
+ children: &[Prehashed<Content>],
getter: fn(StyleChain) -> T,
) -> Option<T> {
let value = getter(styles);
@@ -865,8 +875,8 @@ fn shared_get<T: PartialEq>(
/// Find suitable linebreaks.
fn linebreak<'a>(vt: &Vt, p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> {
- let linebreaks = ParElem::linebreaks_in(p.styles).unwrap_or_else(|| {
- if ParElem::justify_in(p.styles) {
+ let linebreaks = p.linebreaks.unwrap_or_else(|| {
+ if p.justify {
Linebreaks::Optimized
} else {
Linebreaks::Simple
@@ -883,7 +893,7 @@ fn linebreak<'a>(vt: &Vt, p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> {
/// lines greedily, always taking the longest possible line. This may lead to
/// very unbalanced line, but is fast and simple.
fn linebreak_simple<'a>(vt: &Vt, p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> {
- let mut lines = vec![];
+ let mut lines = Vec::with_capacity(16);
let mut start = 0;
let mut last = None;
@@ -964,8 +974,8 @@ fn linebreak_optimized<'a>(vt: &Vt, p: &'a Preparation<'a>, width: Abs) -> Vec<L
line: line(vt, p, 0..0, Breakpoint::Mandatory),
}];
- let em = TextElem::size_in(p.styles);
-
+ let em = p.size;
+ let mut lines = Vec::with_capacity(16);
breakpoints(p, |end, breakpoint| {
let k = table.len();
let eof = end == p.bidi.text.len();
@@ -1071,7 +1081,6 @@ fn linebreak_optimized<'a>(vt: &Vt, p: &'a Preparation<'a>, width: Abs) -> Vec<L
});
// Retrace the best path.
- let mut lines = vec![];
let mut idx = table.len() - 1;
while idx != 0 {
table.truncate(idx + 1);
@@ -1151,7 +1160,7 @@ fn line<'a>(
if hyphen || start < range.end || before.is_empty() {
let mut reshaped = shaped.reshape(vt, &p.spans, start..range.end);
if hyphen || shy {
- reshaped.push_hyphen(vt, TextElem::fallback_in(p.styles));
+ reshaped.push_hyphen(vt, p.fallback);
}
if let Some(last_glyph) = reshaped.glyphs.last() {
@@ -1287,11 +1296,10 @@ fn finalize(
.collect::<SourceResult<_>>()?;
// Prevent orphans.
- let leading = ParElem::leading_in(p.styles);
if frames.len() >= 2 && !frames[1].is_empty() {
let second = frames.remove(1);
let first = &mut frames[0];
- merge(first, second, leading);
+ merge(first, second, p.leading);
}
// Prevent widows.
@@ -1299,7 +1307,7 @@ fn finalize(
if len >= 2 && !frames[len - 2].is_empty() {
let second = frames.pop().unwrap();
let first = frames.last_mut().unwrap();
- merge(first, second, leading);
+ merge(first, second, p.leading);
}
Ok(Fragment::frames(frames))
diff --git a/crates/typst-library/src/layout/place.rs b/crates/typst-library/src/layout/place.rs
index 64cbd9a8..c8e83383 100644
--- a/crates/typst-library/src/layout/place.rs
+++ b/crates/typst-library/src/layout/place.rs
@@ -106,7 +106,10 @@ impl Layout for PlaceElem {
.at(self.span());
}
- let child = self.body().aligned(alignment.unwrap_or_else(|| Align::CENTER));
+ let child = self
+ .body()
+ .clone()
+ .aligned(alignment.unwrap_or_else(|| Align::CENTER));
let pod = Regions::one(base, Axes::splat(false));
let frame = child.layout(vt, styles, pod)?.into_frame();
diff --git a/crates/typst-library/src/layout/spacing.rs b/crates/typst-library/src/layout/spacing.rs
index 37f1ed35..88b6e2cd 100644
--- a/crates/typst-library/src/layout/spacing.rs
+++ b/crates/typst-library/src/layout/spacing.rs
@@ -1,3 +1,5 @@
+use std::borrow::Cow;
+
use crate::prelude::*;
/// Inserts horizontal spacing into a paragraph.
@@ -71,7 +73,7 @@ impl Behave for HElem {
fn larger(
&self,
- prev: &(Content, Behaviour, StyleChain),
+ prev: &(Cow<Content>, Behaviour, StyleChain),
styles: StyleChain,
) -> bool {
let Some(other) = prev.0.to::<Self>() else { return false };
@@ -173,7 +175,7 @@ impl Behave for VElem {
fn larger(
&self,
- prev: &(Content, Behaviour, StyleChain),
+ prev: &(Cow<Content>, Behaviour, StyleChain),
styles: StyleChain,
) -> bool {
let Some(other) = prev.0.to::<Self>() else { return false };
diff --git a/crates/typst-library/src/layout/stack.rs b/crates/typst-library/src/layout/stack.rs
index 398341b2..50d1c862 100644
--- a/crates/typst-library/src/layout/stack.rs
+++ b/crates/typst-library/src/layout/stack.rs
@@ -60,7 +60,7 @@ impl Layout for StackElem {
for child in self.children() {
match child {
StackChild::Spacing(kind) => {
- layouter.layout_spacing(kind);
+ layouter.layout_spacing(*kind);
deferred = None;
}
StackChild::Block(block) => {
@@ -68,7 +68,7 @@ impl Layout for StackElem {
layouter.layout_spacing(kind);
}
- layouter.layout_block(vt, &block, styles)?;
+ layouter.layout_block(vt, block, styles)?;
deferred = spacing;
}
}
@@ -79,7 +79,7 @@ impl Layout for StackElem {
}
/// A child of a stack element.
-#[derive(Hash)]
+#[derive(Clone, PartialEq, Hash)]
pub enum StackChild {
/// Spacing between other children.
Spacing(Spacing),
diff --git a/crates/typst-library/src/layout/table.rs b/crates/typst-library/src/layout/table.rs
index 056b63a2..9e7da071 100644
--- a/crates/typst-library/src/layout/table.rs
+++ b/crates/typst-library/src/layout/table.rs
@@ -1,7 +1,7 @@
use typst::eval::{CastInfo, Reflect};
use crate::layout::{AlignElem, GridLayouter, TrackSizings};
-use crate::meta::{Figurable, LocalName};
+use crate::meta::Figurable;
use crate::prelude::*;
/// A table of items.
@@ -38,10 +38,12 @@ use crate::prelude::*;
pub struct TableElem {
/// The column sizes. See the [grid documentation]($grid) for more
/// information on track sizing.
+ #[borrowed]
pub columns: TrackSizings,
/// The row sizes. See the [grid documentation]($grid) for more information
/// on track sizing.
+ #[borrowed]
pub rows: TrackSizings,
/// The gaps between rows & columns. See the [grid documentation]($grid) for
@@ -51,6 +53,7 @@ pub struct TableElem {
/// The gaps between columns. Takes precedence over `gutter`. See the
/// [grid documentation]($grid) for more information on gutters.
+ #[borrowed]
#[parse(
let gutter = args.named("gutter")?;
args.named("column-gutter")?.or_else(|| gutter.clone())
@@ -60,6 +63,7 @@ pub struct TableElem {
/// The gaps between rows. Takes precedence over `gutter`. See the
/// [grid documentation]($grid) for more information on gutters.
#[parse(args.named("row-gutter")?.or_else(|| gutter.clone()))]
+ #[borrowed]
pub row_gutter: TrackSizings,
/// How to fill the cells.
@@ -82,6 +86,7 @@ pub struct TableElem {
/// [Profit:], [500 €], [1000 €], [1500 €],
/// )
/// ```
+ #[borrowed]
pub fill: Celled<Option<Paint>>,
/// How to align the cells' content.
@@ -99,6 +104,7 @@ pub struct TableElem {
/// [A], [B], [C],
/// )
/// ```
+ #[borrowed]
pub align: Celled<Smart<Align>>,
/// How to [stroke]($stroke) the cells.
@@ -151,16 +157,20 @@ impl Layout for TableElem {
) -> SourceResult<Fragment> {
let inset = self.inset(styles);
let align = self.align(styles);
+ let columns = self.columns(styles);
+ let rows = self.rows(styles);
+ let column_gutter = self.column_gutter(styles);
+ let row_gutter = self.row_gutter(styles);
- let tracks = Axes::new(self.columns(styles).0, self.rows(styles).0);
- let gutter = Axes::new(self.column_gutter(styles).0, self.row_gutter(styles).0);
+ let tracks = Axes::new(columns.0.as_slice(), rows.0.as_slice());
+ let gutter = Axes::new(column_gutter.0.as_slice(), row_gutter.0.as_slice());
let cols = tracks.x.len().max(1);
let cells: Vec<_> = self
.children()
- .into_iter()
+ .iter()
.enumerate()
.map(|(i, child)| {
- let mut child = child.padded(inset);
+ let mut child = child.clone().padded(inset);
let x = i % cols;
let y = i / cols;
@@ -176,14 +186,8 @@ impl Layout for TableElem {
let stroke = self.stroke(styles).map(Stroke::unwrap_or_default);
// Prepare grid layout by unifying content and gutter tracks.
- let layouter = GridLayouter::new(
- tracks.as_deref(),
- gutter.as_deref(),
- &cells,
- regions,
- styles,
- self.span(),
- );
+ let layouter =
+ GridLayouter::new(tracks, gutter, &cells, regions, styles, self.span());
// Measure the columns and layout the grid row-by-row.
let mut layout = layouter.layout(vt)?;
@@ -321,7 +325,7 @@ impl<T: FromValue> FromValue for Celled<T> {
}
impl LocalName for TableElem {
- fn local_name(&self, lang: Lang, _: Option<Region>) -> &'static str {
+ fn local_name(lang: Lang, _: Option<Region>) -> &'static str {
match lang {
Lang::ALBANIAN => "Tabel",
Lang::ARABIC => "جدول",
diff --git a/crates/typst-library/src/layout/terms.rs b/crates/typst-library/src/layout/terms.rs
index 07f17bb0..d4262118 100644
--- a/crates/typst-library/src/layout/terms.rs
+++ b/crates/typst-library/src/layout/terms.rs
@@ -55,6 +55,7 @@ pub struct TermsElem {
/// / Colon: A nice separator symbol.
/// ```
#[default(HElem::new(Em::new(0.6).into()).with_weak(true).pack())]
+ #[borrowed]
pub separator: Content,
/// The indentation of each item.
@@ -114,20 +115,20 @@ impl Layout for TermsElem {
ParElem::leading_in(styles).into()
} else {
self.spacing(styles)
- .unwrap_or_else(|| BlockElem::below_in(styles).amount())
+ .unwrap_or_else(|| *BlockElem::below_in(styles).amount())
};
let mut seq = vec![];
- for (i, child) in self.children().into_iter().enumerate() {
+ for (i, child) in self.children().iter().enumerate() {
if i > 0 {
seq.push(VElem::new(gutter).with_weakness(1).pack());
}
if !indent.is_zero() {
seq.push(HElem::new(indent.into()).pack());
}
- seq.push(child.term().strong());
- seq.push(separator.clone());
- seq.push(child.description());
+ seq.push(child.term().clone().strong());
+ seq.push((*separator).clone());
+ seq.push(child.description().clone());
}
Content::sequence(seq)