diff options
Diffstat (limited to 'crates/typst-library/src/layout')
| -rw-r--r-- | crates/typst-library/src/layout/align.rs | 5 | ||||
| -rw-r--r-- | crates/typst-library/src/layout/enum.rs | 8 | ||||
| -rw-r--r-- | crates/typst-library/src/layout/flow.rs | 10 | ||||
| -rw-r--r-- | crates/typst-library/src/layout/grid.rs | 24 | ||||
| -rw-r--r-- | crates/typst-library/src/layout/hide.rs | 4 | ||||
| -rw-r--r-- | crates/typst-library/src/layout/list.rs | 8 | ||||
| -rw-r--r-- | crates/typst-library/src/layout/mod.rs | 49 | ||||
| -rw-r--r-- | crates/typst-library/src/layout/page.rs | 41 | ||||
| -rw-r--r-- | crates/typst-library/src/layout/par.rs | 62 | ||||
| -rw-r--r-- | crates/typst-library/src/layout/place.rs | 5 | ||||
| -rw-r--r-- | crates/typst-library/src/layout/spacing.rs | 6 | ||||
| -rw-r--r-- | crates/typst-library/src/layout/stack.rs | 6 | ||||
| -rw-r--r-- | crates/typst-library/src/layout/table.rs | 32 | ||||
| -rw-r--r-- | crates/typst-library/src/layout/terms.rs | 11 |
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( - "es, + 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) |
