From 0a3c6939dd274f40672484695d909c2cc0d0d755 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Tue, 8 Jul 2025 10:52:43 +0200 Subject: [PATCH] Rewrite foundations of native elements (#6547) --- .github/workflows/ci.yml | 12 + crates/typst-eval/src/markup.rs | 9 +- crates/typst-eval/src/math.rs | 6 +- crates/typst-eval/src/rules.rs | 5 +- crates/typst-html/src/lib.rs | 14 +- crates/typst-ide/src/definition.rs | 2 +- crates/typst-ide/src/tests.rs | 10 +- crates/typst-layout/src/flow/block.rs | 38 +- crates/typst-layout/src/flow/collect.rs | 42 +- crates/typst-layout/src/flow/compose.rs | 4 +- crates/typst-layout/src/flow/mod.rs | 20 +- crates/typst-layout/src/grid/layouter.rs | 2 +- crates/typst-layout/src/image.rs | 8 +- crates/typst-layout/src/inline/box.rs | 21 +- crates/typst-layout/src/inline/collect.rs | 26 +- crates/typst-layout/src/inline/line.rs | 12 +- crates/typst-layout/src/inline/linebreak.rs | 6 +- crates/typst-layout/src/inline/mod.rs | 50 +- crates/typst-layout/src/inline/shaping.rs | 41 +- crates/typst-layout/src/lists.rs | 50 +- crates/typst-layout/src/math/accent.rs | 4 +- crates/typst-layout/src/math/attach.rs | 19 +- crates/typst-layout/src/math/cancel.rs | 16 +- crates/typst-layout/src/math/frac.rs | 2 +- crates/typst-layout/src/math/fragment.rs | 25 +- crates/typst-layout/src/math/lr.rs | 4 +- crates/typst-layout/src/math/mat.rs | 27 +- crates/typst-layout/src/math/mod.rs | 35 +- crates/typst-layout/src/math/root.rs | 6 +- crates/typst-layout/src/math/run.rs | 6 +- crates/typst-layout/src/math/shared.rs | 53 +- crates/typst-layout/src/math/stretch.rs | 9 +- crates/typst-layout/src/math/text.rs | 12 +- crates/typst-layout/src/math/underover.rs | 18 +- crates/typst-layout/src/modifiers.rs | 8 +- crates/typst-layout/src/pad.rs | 10 +- crates/typst-layout/src/pages/collect.rs | 8 +- crates/typst-layout/src/pages/run.rs | 42 +- crates/typst-layout/src/repeat.rs | 6 +- crates/typst-layout/src/shapes.rs | 96 +- crates/typst-layout/src/stack.rs | 10 +- crates/typst-layout/src/transforms.rs | 24 +- .../src/foundations/{ => content}/element.rs | 220 ++--- .../src/foundations/content/field.rs | 564 ++++++++++++ .../{content.rs => content/mod.rs} | 434 ++------- .../src/foundations/content/packed.rs | 147 ++++ .../src/foundations/content/raw.rs | 426 +++++++++ .../src/foundations/content/vtable.rs | 383 ++++++++ crates/typst-library/src/foundations/mod.rs | 2 - crates/typst-library/src/foundations/scope.rs | 11 +- .../typst-library/src/foundations/selector.rs | 4 +- .../typst-library/src/foundations/styles.rs | 238 +++-- .../typst-library/src/foundations/target.rs | 2 +- crates/typst-library/src/html/mod.rs | 7 +- crates/typst-library/src/html/typed.rs | 4 +- .../src/introspection/counter.rs | 26 +- .../typst-library/src/introspection/state.rs | 4 +- crates/typst-library/src/layout/align.rs | 10 +- crates/typst-library/src/layout/columns.rs | 1 - crates/typst-library/src/layout/container.rs | 11 - crates/typst-library/src/layout/em.rs | 2 +- crates/typst-library/src/layout/grid/mod.rs | 12 +- .../typst-library/src/layout/grid/resolve.rs | 176 ++-- crates/typst-library/src/layout/hide.rs | 2 +- crates/typst-library/src/layout/page.rs | 10 - crates/typst-library/src/layout/place.rs | 1 - crates/typst-library/src/math/accent.rs | 1 - crates/typst-library/src/math/attach.rs | 5 +- crates/typst-library/src/math/cancel.rs | 2 - crates/typst-library/src/math/equation.rs | 40 +- crates/typst-library/src/math/lr.rs | 3 +- crates/typst-library/src/math/matrix.rs | 7 - crates/typst-library/src/math/style.rs | 34 +- .../typst-library/src/model/bibliography.rs | 53 +- crates/typst-library/src/model/cite.rs | 5 +- crates/typst-library/src/model/document.rs | 31 +- crates/typst-library/src/model/emph.rs | 4 +- crates/typst-library/src/model/enum.rs | 18 +- crates/typst-library/src/model/figure.rs | 94 +- crates/typst-library/src/model/footnote.rs | 17 +- crates/typst-library/src/model/heading.rs | 50 +- crates/typst-library/src/model/link.rs | 4 +- crates/typst-library/src/model/list.rs | 12 +- crates/typst-library/src/model/outline.rs | 36 +- crates/typst-library/src/model/par.rs | 3 - crates/typst-library/src/model/quote.rs | 33 +- crates/typst-library/src/model/reference.rs | 15 +- crates/typst-library/src/model/strong.rs | 4 +- crates/typst-library/src/model/table.rs | 18 +- crates/typst-library/src/model/terms.rs | 27 +- crates/typst-library/src/pdf/embed.rs | 5 +- crates/typst-library/src/text/case.rs | 4 +- crates/typst-library/src/text/deco.rs | 113 +-- crates/typst-library/src/text/lang.rs | 2 +- crates/typst-library/src/text/mod.rs | 59 +- crates/typst-library/src/text/raw.rs | 44 +- crates/typst-library/src/text/shift.rs | 33 +- crates/typst-library/src/text/smallcaps.rs | 5 +- crates/typst-library/src/text/smartquote.rs | 3 +- crates/typst-library/src/visualize/curve.rs | 1 - .../typst-library/src/visualize/image/mod.rs | 17 +- crates/typst-library/src/visualize/line.rs | 4 - crates/typst-library/src/visualize/path.rs | 1 - crates/typst-library/src/visualize/polygon.rs | 5 +- crates/typst-library/src/visualize/shape.rs | 34 +- crates/typst-macros/src/elem.rs | 821 +++++------------- crates/typst-pdf/src/convert.rs | 2 +- crates/typst-pdf/src/embed.rs | 18 +- crates/typst-pdf/src/outline.rs | 7 +- crates/typst-realize/src/lib.rs | 50 +- crates/typst-utils/src/hash.rs | 99 ++- crates/typst-utils/src/lib.rs | 2 +- crates/typst/src/lib.rs | 2 +- docs/src/lib.rs | 8 +- tests/src/world.rs | 10 +- 115 files changed, 3103 insertions(+), 2277 deletions(-) rename crates/typst-library/src/foundations/{ => content}/element.rs (53%) create mode 100644 crates/typst-library/src/foundations/content/field.rs rename crates/typst-library/src/foundations/{content.rs => content/mod.rs} (64%) create mode 100644 crates/typst-library/src/foundations/content/packed.rs create mode 100644 crates/typst-library/src/foundations/content/raw.rs create mode 100644 crates/typst-library/src/foundations/content/vtable.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2354de582..70518860e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -103,3 +103,15 @@ jobs: - uses: Swatinem/rust-cache@v2 - run: cargo install --locked cargo-fuzz@0.12.0 - run: cd tests/fuzz && cargo fuzz build --dev + + miri: + name: Check unsafe code + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + components: miri + toolchain: nightly-2024-10-29 + - uses: Swatinem/rust-cache@v2 + - run: cargo miri test -p typst-library test_miri diff --git a/crates/typst-eval/src/markup.rs b/crates/typst-eval/src/markup.rs index 9118ded56..cc9606269 100644 --- a/crates/typst-eval/src/markup.rs +++ b/crates/typst-eval/src/markup.rs @@ -186,7 +186,7 @@ impl Eval for ast::Raw<'_> { let lines = self.lines().map(|line| (line.get().clone(), line.span())).collect(); let mut elem = RawElem::new(RawContent::Lines(lines)).with_block(self.block()); if let Some(lang) = self.lang() { - elem.push_lang(Some(lang.get().clone())); + elem.lang.set(Some(lang.get().clone())); } Ok(elem.pack()) } @@ -219,9 +219,8 @@ impl Eval for ast::Ref<'_> { .expect("unexpected empty reference"); let mut elem = RefElem::new(target); if let Some(supplement) = self.supplement() { - elem.push_supplement(Smart::Custom(Some(Supplement::Content( - supplement.eval(vm)?, - )))); + elem.supplement + .set(Smart::Custom(Some(Supplement::Content(supplement.eval(vm)?)))); } Ok(elem.pack()) } @@ -252,7 +251,7 @@ impl Eval for ast::EnumItem<'_> { let body = self.body().eval(vm)?; let mut elem = EnumItem::new(body); if let Some(number) = self.number() { - elem.push_number(Some(number)); + elem.number.set(Some(number)); } Ok(elem.pack()) } diff --git a/crates/typst-eval/src/math.rs b/crates/typst-eval/src/math.rs index 0e271a089..c2325a8c5 100644 --- a/crates/typst-eval/src/math.rs +++ b/crates/typst-eval/src/math.rs @@ -80,17 +80,17 @@ impl Eval for ast::MathAttach<'_> { let mut elem = AttachElem::new(base); if let Some(expr) = self.top() { - elem.push_t(Some(expr.eval_display(vm)?)); + elem.t.set(Some(expr.eval_display(vm)?)); } // Always attach primes in scripts style (not limits style), // i.e. at the top-right corner. if let Some(primes) = self.primes() { - elem.push_tr(Some(primes.eval(vm)?)); + elem.tr.set(Some(primes.eval(vm)?)); } if let Some(expr) = self.bottom() { - elem.push_b(Some(expr.eval_display(vm)?)); + elem.b.set(Some(expr.eval_display(vm)?)); } Ok(elem.pack()) diff --git a/crates/typst-eval/src/rules.rs b/crates/typst-eval/src/rules.rs index f4c1563f3..eb6a1e6da 100644 --- a/crates/typst-eval/src/rules.rs +++ b/crates/typst-eval/src/rules.rs @@ -1,6 +1,6 @@ use typst_library::diag::{warning, At, SourceResult}; use typst_library::foundations::{ - Element, Fields, Func, Recipe, Selector, ShowableSelector, Styles, Transformation, + Element, Func, Recipe, Selector, ShowableSelector, Styles, Transformation, }; use typst_library::layout::BlockElem; use typst_library::model::ParElem; @@ -62,8 +62,7 @@ fn check_show_par_set_block(vm: &mut Vm, recipe: &Recipe) { if let Some(Selector::Elem(elem, _)) = recipe.selector(); if *elem == Element::of::(); if let Transformation::Style(styles) = recipe.transform(); - if styles.has::(::Enum::Above as _) || - styles.has::(::Enum::Below as _); + if styles.has(BlockElem::above) || styles.has(BlockElem::below); then { vm.engine.sink.warn(warning!( recipe.span(), diff --git a/crates/typst-html/src/lib.rs b/crates/typst-html/src/lib.rs index 495187166..60ffa78ee 100644 --- a/crates/typst-html/src/lib.rs +++ b/crates/typst-html/src/lib.rs @@ -177,12 +177,12 @@ fn handle( output.push(HtmlNode::Tag(elem.tag.clone())); } else if let Some(elem) = child.to_packed::() { let mut children = vec![]; - if let Some(body) = elem.body(styles) { + if let Some(body) = elem.body.get_ref(styles) { children = html_fragment(engine, body, locator.next(&elem.span()), styles)?; } let element = HtmlElement { tag: elem.tag, - attrs: elem.attrs(styles).clone(), + attrs: elem.attrs.get_cloned(styles), children, span: elem.span(), }; @@ -198,7 +198,7 @@ fn handle( ); } else if let Some(elem) = child.to_packed::() { // TODO: This is rather incomplete. - if let Some(body) = elem.body(styles) { + if let Some(body) = elem.body.get_ref(styles) { let children = html_fragment(engine, body, locator.next(&elem.span()), styles)?; output.push( @@ -212,7 +212,7 @@ fn handle( } else if let Some((elem, body)) = child .to_packed::() - .and_then(|elem| match elem.body(styles) { + .and_then(|elem| match elem.body.get_ref(styles) { Some(BlockBody::Content(body)) => Some((elem, body)), _ => None, }) @@ -233,12 +233,12 @@ fn handle( output.push(HtmlElement::new(tag::br).spanned(elem.span()).into()); } else if let Some(elem) = child.to_packed::() { output.push(HtmlNode::text( - if elem.double(styles) { '"' } else { '\'' }, + if elem.double.get(styles) { '"' } else { '\'' }, child.span(), )); } else if let Some(elem) = child.to_packed::() { let locator = locator.next(&elem.span()); - let style = TargetElem::set_target(Target::Paged).wrap(); + let style = TargetElem::target.set(Target::Paged).wrap(); let frame = (engine.routines.layout_frame)( engine, &elem.body, @@ -248,7 +248,7 @@ fn handle( )?; output.push(HtmlNode::Frame(HtmlFrame { inner: frame, - text_size: TextElem::size_in(styles), + text_size: styles.resolve(TextElem::size), })); } else { engine.sink.warn(warning!( diff --git a/crates/typst-ide/src/definition.rs b/crates/typst-ide/src/definition.rs index ae1ba287b..4c2b80cd4 100644 --- a/crates/typst-ide/src/definition.rs +++ b/crates/typst-ide/src/definition.rs @@ -187,6 +187,6 @@ mod tests { #[test] fn test_definition_std() { - test("#table", 1, Side::After).must_be_value(typst::model::TableElem::elem()); + test("#table", 1, Side::After).must_be_value(typst::model::TableElem::ELEM); } } diff --git a/crates/typst-ide/src/tests.rs b/crates/typst-ide/src/tests.rs index dd5c230ad..b3f368f2e 100644 --- a/crates/typst-ide/src/tests.rs +++ b/crates/typst-ide/src/tests.rs @@ -171,13 +171,11 @@ fn library() -> Library { let mut lib = typst::Library::builder() .with_features([Feature::Html].into_iter().collect()) .build(); + lib.styles.set(PageElem::width, Smart::Custom(Abs::pt(120.0).into())); + lib.styles.set(PageElem::height, Smart::Auto); lib.styles - .set(PageElem::set_width(Smart::Custom(Abs::pt(120.0).into()))); - lib.styles.set(PageElem::set_height(Smart::Auto)); - lib.styles.set(PageElem::set_margin(Margin::splat(Some(Smart::Custom( - Abs::pt(10.0).into(), - ))))); - lib.styles.set(TextElem::set_size(TextSize(Abs::pt(10.0).into()))); + .set(PageElem::margin, Margin::splat(Some(Smart::Custom(Abs::pt(10.0).into())))); + lib.styles.set(TextElem::size, TextSize(Abs::pt(10.0).into())); lib } diff --git a/crates/typst-layout/src/flow/block.rs b/crates/typst-layout/src/flow/block.rs index 6c2c3923d..d6cfe3a9e 100644 --- a/crates/typst-layout/src/flow/block.rs +++ b/crates/typst-layout/src/flow/block.rs @@ -24,15 +24,15 @@ pub fn layout_single_block( region: Region, ) -> SourceResult { // Fetch sizing properties. - let width = elem.width(styles); - let height = elem.height(styles); - let inset = elem.inset(styles).unwrap_or_default(); + let width = elem.width.get(styles); + let height = elem.height.get(styles); + let inset = elem.inset.resolve(styles).unwrap_or_default(); // Build the pod regions. let pod = unbreakable_pod(&width.into(), &height, &inset, styles, region.size); // Layout the body. - let body = elem.body(styles); + let body = elem.body.get_ref(styles); let mut frame = match body { // If we have no body, just create one frame. Its size will be // adjusted below. @@ -73,18 +73,19 @@ pub fn layout_single_block( } // Prepare fill and stroke. - let fill = elem.fill(styles); + let fill = elem.fill.get_cloned(styles); let stroke = elem - .stroke(styles) + .stroke + .resolve(styles) .unwrap_or_default() .map(|s| s.map(Stroke::unwrap_or_default)); // Only fetch these if necessary (for clipping or filling/stroking). - let outset = LazyCell::new(|| elem.outset(styles).unwrap_or_default()); - let radius = LazyCell::new(|| elem.radius(styles).unwrap_or_default()); + let outset = LazyCell::new(|| elem.outset.resolve(styles).unwrap_or_default()); + let radius = LazyCell::new(|| elem.radius.resolve(styles).unwrap_or_default()); // Clip the contents, if requested. - if elem.clip(styles) { + if elem.clip.get(styles) { frame.clip(clip_rect(frame.size(), &radius, &stroke, &outset)); } @@ -111,9 +112,9 @@ pub fn layout_multi_block( regions: Regions, ) -> SourceResult { // Fetch sizing properties. - let width = elem.width(styles); - let height = elem.height(styles); - let inset = elem.inset(styles).unwrap_or_default(); + let width = elem.width.get(styles); + let height = elem.height.get(styles); + let inset = elem.inset.resolve(styles).unwrap_or_default(); // Allocate a small vector for backlogs. let mut buf = SmallVec::<[Abs; 2]>::new(); @@ -122,7 +123,7 @@ pub fn layout_multi_block( let pod = breakable_pod(&width.into(), &height, &inset, styles, regions, &mut buf); // Layout the body. - let body = elem.body(styles); + let body = elem.body.get_ref(styles); let mut fragment = match body { // If we have no body, just create one frame plus one per backlog // region. We create them zero-sized; if necessary, their size will @@ -188,18 +189,19 @@ pub fn layout_multi_block( }; // Prepare fill and stroke. - let fill = elem.fill(styles); + let fill = elem.fill.get_ref(styles); let stroke = elem - .stroke(styles) + .stroke + .resolve(styles) .unwrap_or_default() .map(|s| s.map(Stroke::unwrap_or_default)); // Only fetch these if necessary (for clipping or filling/stroking). - let outset = LazyCell::new(|| elem.outset(styles).unwrap_or_default()); - let radius = LazyCell::new(|| elem.radius(styles).unwrap_or_default()); + let outset = LazyCell::new(|| elem.outset.resolve(styles).unwrap_or_default()); + let radius = LazyCell::new(|| elem.radius.resolve(styles).unwrap_or_default()); // Fetch/compute these outside of the loop. - let clip = elem.clip(styles); + let clip = elem.clip.get(styles); let has_fill_or_stroke = fill.is_some() || stroke.iter().any(Option::is_some); let has_inset = !inset.is_zero(); let is_explicit = matches!(body, None | Some(BlockBody::Content(_))); diff --git a/crates/typst-layout/src/flow/collect.rs b/crates/typst-layout/src/flow/collect.rs index 2c14f7a37..76268b590 100644 --- a/crates/typst-layout/src/flow/collect.rs +++ b/crates/typst-layout/src/flow/collect.rs @@ -89,7 +89,7 @@ impl<'a> Collector<'a, '_, '_> { } else if child.is::() { self.output.push(Child::Flush); } else if let Some(elem) = child.to_packed::() { - self.output.push(Child::Break(elem.weak(styles))); + self.output.push(Child::Break(elem.weak.get(styles))); } else if child.is::() { bail!( child.span(), "pagebreaks are not allowed inside of containers"; @@ -132,7 +132,7 @@ impl<'a> Collector<'a, '_, '_> { self.output.push(Child::Tag(&elem.tag)); } - let leading = ParElem::leading_in(styles); + let leading = styles.resolve(ParElem::leading); self.lines(lines, leading, styles); for (c, _) in &self.children[end..] { @@ -146,7 +146,9 @@ impl<'a> Collector<'a, '_, '_> { /// Collect vertical spacing into a relative or fractional child. fn v(&mut self, elem: &'a Packed, styles: StyleChain<'a>) { self.output.push(match elem.amount { - Spacing::Rel(rel) => Child::Rel(rel.resolve(styles), elem.weak(styles) as u8), + Spacing::Rel(rel) => { + Child::Rel(rel.resolve(styles), elem.weak.get(styles) as u8) + } Spacing::Fr(fr) => Child::Fr(fr), }); } @@ -169,8 +171,8 @@ impl<'a> Collector<'a, '_, '_> { )? .into_frames(); - let spacing = elem.spacing(styles); - let leading = elem.leading(styles); + let spacing = elem.spacing.resolve(styles); + let leading = elem.leading.resolve(styles); self.output.push(Child::Rel(spacing.into(), 4)); @@ -184,8 +186,8 @@ impl<'a> Collector<'a, '_, '_> { /// Collect laid-out lines. fn lines(&mut self, lines: Vec, leading: Abs, styles: StyleChain<'a>) { - let align = AlignElem::alignment_in(styles).resolve(styles); - let costs = TextElem::costs_in(styles); + let align = styles.resolve(AlignElem::alignment); + let costs = styles.get(TextElem::costs); // Determine whether to prevent widow and orphans. let len = lines.len(); @@ -231,23 +233,23 @@ impl<'a> Collector<'a, '_, '_> { /// whether it is breakable. fn block(&mut self, elem: &'a Packed, styles: StyleChain<'a>) { let locator = self.locator.next(&elem.span()); - let align = AlignElem::alignment_in(styles).resolve(styles); + let align = styles.resolve(AlignElem::alignment); let alone = self.children.len() == 1; - let sticky = elem.sticky(styles); - let breakable = elem.breakable(styles); - let fr = match elem.height(styles) { + let sticky = elem.sticky.get(styles); + let breakable = elem.breakable.get(styles); + let fr = match elem.height.get(styles) { Sizing::Fr(fr) => Some(fr), _ => None, }; - let fallback = LazyCell::new(|| ParElem::spacing_in(styles)); + let fallback = LazyCell::new(|| styles.resolve(ParElem::spacing)); let spacing = |amount| match amount { Smart::Auto => Child::Rel((*fallback).into(), 4), Smart::Custom(Spacing::Rel(rel)) => Child::Rel(rel.resolve(styles), 3), Smart::Custom(Spacing::Fr(fr)) => Child::Fr(fr), }; - self.output.push(spacing(elem.above(styles))); + self.output.push(spacing(elem.above.get(styles))); if !breakable || fr.is_some() { self.output.push(Child::Single(self.boxed(SingleChild { @@ -272,7 +274,7 @@ impl<'a> Collector<'a, '_, '_> { }))); }; - self.output.push(spacing(elem.below(styles))); + self.output.push(spacing(elem.below.get(styles))); self.par_situation = ParSituation::Other; } @@ -282,13 +284,13 @@ impl<'a> Collector<'a, '_, '_> { elem: &'a Packed, styles: StyleChain<'a>, ) -> SourceResult<()> { - let alignment = elem.alignment(styles); + let alignment = elem.alignment.get(styles); let align_x = alignment.map_or(FixedAlignment::Center, |align| { align.x().unwrap_or_default().resolve(styles) }); let align_y = alignment.map(|align| align.y().map(|y| y.resolve(styles))); - let scope = elem.scope(styles); - let float = elem.float(styles); + let scope = elem.scope.get(styles); + let float = elem.float.get(styles); match (float, align_y) { (true, Smart::Custom(None | Some(FixedAlignment::Center))) => bail!( @@ -312,8 +314,8 @@ impl<'a> Collector<'a, '_, '_> { } let locator = self.locator.next(&elem.span()); - let clearance = elem.clearance(styles); - let delta = Axes::new(elem.dx(styles), elem.dy(styles)).resolve(styles); + let clearance = elem.clearance.resolve(styles); + let delta = Axes::new(elem.dx.get(styles), elem.dy.get(styles)).resolve(styles); self.output.push(Child::Placed(self.boxed(PlacedChild { align_x, align_y, @@ -631,7 +633,7 @@ impl PlacedChild<'_> { pub fn layout(&self, engine: &mut Engine, base: Size) -> SourceResult { self.cell.get_or_init(base, |base| { let align = self.alignment.unwrap_or_else(|| Alignment::CENTER); - let aligned = AlignElem::set_alignment(align).wrap(); + let aligned = AlignElem::alignment.set(align).wrap(); let styles = self.styles.chain(&aligned); let mut frame = layout_and_modify(styles, |styles| { diff --git a/crates/typst-layout/src/flow/compose.rs b/crates/typst-layout/src/flow/compose.rs index 54dc487a3..ed514a248 100644 --- a/crates/typst-layout/src/flow/compose.rs +++ b/crates/typst-layout/src/flow/compose.rs @@ -851,7 +851,7 @@ fn layout_line_number_reset( config: &Config, locator: &mut SplitLocator, ) -> SourceResult { - let counter = Counter::of(ParLineMarker::elem()); + let counter = Counter::of(ParLineMarker::ELEM); let update = CounterUpdate::Set(CounterState::init(false)); let content = counter.update(Span::detached(), update); crate::layout_frame( @@ -879,7 +879,7 @@ fn layout_line_number( locator: &mut SplitLocator, numbering: &Numbering, ) -> SourceResult { - let counter = Counter::of(ParLineMarker::elem()); + let counter = Counter::of(ParLineMarker::ELEM); let update = CounterUpdate::Step(NonZeroUsize::ONE); let numbering = Smart::Custom(numbering.clone()); diff --git a/crates/typst-layout/src/flow/mod.rs b/crates/typst-layout/src/flow/mod.rs index cba228bcd..f4f1c0915 100644 --- a/crates/typst-layout/src/flow/mod.rs +++ b/crates/typst-layout/src/flow/mod.rs @@ -98,8 +98,8 @@ pub fn layout_columns( locator.track(), styles, regions, - elem.count(styles), - elem.gutter(styles), + elem.count.get(styles), + elem.gutter.resolve(styles), ) } @@ -251,22 +251,22 @@ fn configuration<'x>( let gutter = column_gutter.relative_to(regions.base().x); let width = (regions.size.x - gutter * (count - 1) as f64) / count as f64; - let dir = TextElem::dir_in(shared); + let dir = shared.resolve(TextElem::dir); ColumnConfig { count, width, gutter, dir } }, footnote: FootnoteConfig { - separator: FootnoteEntry::separator_in(shared), - clearance: FootnoteEntry::clearance_in(shared), - gap: FootnoteEntry::gap_in(shared), + separator: shared.get_cloned(FootnoteEntry::separator), + clearance: shared.resolve(FootnoteEntry::clearance), + gap: shared.resolve(FootnoteEntry::gap), expand: regions.expand.x, }, line_numbers: (mode == FlowMode::Root).then(|| LineNumberConfig { - scope: ParLine::numbering_scope_in(shared), + scope: shared.get(ParLine::numbering_scope), default_clearance: { - let width = if PageElem::flipped_in(shared) { - PageElem::height_in(shared) + let width = if shared.get(PageElem::flipped) { + shared.resolve(PageElem::height) } else { - PageElem::width_in(shared) + shared.resolve(PageElem::width) }; // Clamp below is safe (min <= max): if the font size is diff --git a/crates/typst-layout/src/grid/layouter.rs b/crates/typst-layout/src/grid/layouter.rs index 42fe38dbe..d4f11f470 100644 --- a/crates/typst-layout/src/grid/layouter.rs +++ b/crates/typst-layout/src/grid/layouter.rs @@ -249,7 +249,7 @@ impl<'a> GridLayouter<'a> { rowspans: vec![], finished: vec![], finished_header_rows: vec![], - is_rtl: TextElem::dir_in(styles) == Dir::RTL, + is_rtl: styles.resolve(TextElem::dir) == Dir::RTL, repeating_headers: vec![], upcoming_headers: &grid.headers, pending_headers: Default::default(), diff --git a/crates/typst-layout/src/image.rs b/crates/typst-layout/src/image.rs index a8f4a0c81..261a58fa3 100644 --- a/crates/typst-layout/src/image.rs +++ b/crates/typst-layout/src/image.rs @@ -28,7 +28,7 @@ pub fn layout_image( // Take the format that was explicitly defined, or parse the extension, // or try to detect the format. let Derived { source, derived: loaded } = &elem.source; - let format = match elem.format(styles) { + let format = match elem.format.get(styles) { Smart::Custom(v) => v, Smart::Auto => determine_format(source, &loaded.data).at(span)?, }; @@ -55,7 +55,7 @@ pub fn layout_image( RasterImage::new( loaded.data.clone(), format, - elem.icc(styles).as_ref().map(|icc| icc.derived.clone()), + elem.icc.get_ref(styles).as_ref().map(|icc| icc.derived.clone()), ) .at(span)?, ), @@ -69,7 +69,7 @@ pub fn layout_image( ), }; - let image = Image::new(kind, elem.alt(styles), elem.scaling(styles)); + let image = Image::new(kind, elem.alt.get_cloned(styles), elem.scaling.get(styles)); // Determine the image's pixel aspect ratio. let pxw = image.width(); @@ -106,7 +106,7 @@ pub fn layout_image( }; // Compute the actual size of the fitted image. - let fit = elem.fit(styles); + let fit = elem.fit.get(styles); let fitted = match fit { ImageFit::Cover | ImageFit::Contain => { if wide == (fit == ImageFit::Contain) { diff --git a/crates/typst-layout/src/inline/box.rs b/crates/typst-layout/src/inline/box.rs index e21928d3c..65b025334 100644 --- a/crates/typst-layout/src/inline/box.rs +++ b/crates/typst-layout/src/inline/box.rs @@ -21,15 +21,15 @@ pub fn layout_box( region: Size, ) -> SourceResult { // Fetch sizing properties. - let width = elem.width(styles); - let height = elem.height(styles); - let inset = elem.inset(styles).unwrap_or_default(); + let width = elem.width.get(styles); + let height = elem.height.get(styles); + let inset = elem.inset.resolve(styles).unwrap_or_default(); // Build the pod region. let pod = unbreakable_pod(&width, &height.into(), &inset, styles, region); // Layout the body. - let mut frame = match elem.body(styles) { + let mut frame = match elem.body.get_ref(styles) { // If we have no body, just create an empty frame. If necessary, // its size will be adjusted below. None => Frame::hard(Size::zero()), @@ -50,18 +50,19 @@ pub fn layout_box( } // Prepare fill and stroke. - let fill = elem.fill(styles); + let fill = elem.fill.get_cloned(styles); let stroke = elem - .stroke(styles) + .stroke + .resolve(styles) .unwrap_or_default() .map(|s| s.map(Stroke::unwrap_or_default)); // Only fetch these if necessary (for clipping or filling/stroking). - let outset = LazyCell::new(|| elem.outset(styles).unwrap_or_default()); - let radius = LazyCell::new(|| elem.radius(styles).unwrap_or_default()); + let outset = LazyCell::new(|| elem.outset.resolve(styles).unwrap_or_default()); + let radius = LazyCell::new(|| elem.radius.resolve(styles).unwrap_or_default()); // Clip the contents, if requested. - if elem.clip(styles) { + if elem.clip.get(styles) { frame.clip(clip_rect(frame.size(), &radius, &stroke, &outset)); } @@ -78,7 +79,7 @@ pub fn layout_box( // Apply baseline shift. Do this after setting the size and applying the // inset, so that a relative shift is resolved relative to the final // height. - let shift = elem.baseline(styles).relative_to(frame.height()); + let shift = elem.baseline.resolve(styles).relative_to(frame.height()); if !shift.is_zero() { frame.set_baseline(frame.baseline() - shift); } diff --git a/crates/typst-layout/src/inline/collect.rs b/crates/typst-layout/src/inline/collect.rs index 829f64b66..2744b31e0 100644 --- a/crates/typst-layout/src/inline/collect.rs +++ b/crates/typst-layout/src/inline/collect.rs @@ -144,7 +144,7 @@ pub fn collect<'a>( collector.push_text(" ", styles); } else if let Some(elem) = child.to_packed::() { collector.build_text(styles, |full| { - let dir = TextElem::dir_in(styles); + let dir = styles.resolve(TextElem::dir); if dir != config.dir { // Insert "Explicit Directional Embedding". match dir { @@ -154,7 +154,7 @@ pub fn collect<'a>( } } - if let Some(case) = TextElem::case_in(styles) { + if let Some(case) = styles.get(TextElem::case) { full.push_str(&case.apply(&elem.text)); } else { full.push_str(&elem.text); @@ -174,20 +174,22 @@ pub fn collect<'a>( Spacing::Fr(fr) => Item::Fractional(fr, None), Spacing::Rel(rel) => Item::Absolute( rel.resolve(styles).relative_to(region.x), - elem.weak(styles), + elem.weak.get(styles), ), }); } else if let Some(elem) = child.to_packed::() { - collector - .push_text(if elem.justify(styles) { "\u{2028}" } else { "\n" }, styles); + collector.push_text( + if elem.justify.get(styles) { "\u{2028}" } else { "\n" }, + styles, + ); } else if let Some(elem) = child.to_packed::() { - let double = elem.double(styles); - if elem.enabled(styles) { + let double = elem.double.get(styles); + if elem.enabled.get(styles) { let quotes = SmartQuotes::get( - elem.quotes(styles), - TextElem::lang_in(styles), - TextElem::region_in(styles), - elem.alternative(styles), + elem.quotes.get_ref(styles), + styles.get(TextElem::lang), + styles.get(TextElem::region), + elem.alternative.get(styles), ); let before = collector.full.chars().rev().find(|&c| !is_default_ignorable(c)); @@ -215,7 +217,7 @@ pub fn collect<'a>( collector.push_item(Item::Skip(POP_ISOLATE)); } else if let Some(elem) = child.to_packed::() { let loc = locator.next(&elem.span()); - if let Sizing::Fr(v) = elem.width(styles) { + if let Sizing::Fr(v) = elem.width.get(styles) { collector.push_item(Item::Fractional(v, Some((elem, loc, styles)))); } else { let mut frame = layout_and_modify(styles, |styles| { diff --git a/crates/typst-layout/src/inline/line.rs b/crates/typst-layout/src/inline/line.rs index b850e50ee..58162d12b 100644 --- a/crates/typst-layout/src/inline/line.rs +++ b/crates/typst-layout/src/inline/line.rs @@ -2,6 +2,7 @@ use std::fmt::{self, Debug, Formatter}; use std::ops::{Deref, DerefMut}; use typst_library::engine::Engine; +use typst_library::foundations::Resolve; use typst_library::introspection::{SplitLocator, Tag}; use typst_library::layout::{Abs, Dir, Em, Fr, Frame, FrameItem, Point}; use typst_library::model::ParLineMarker; @@ -418,10 +419,11 @@ pub fn apply_shift<'a>( frame: &mut Frame, styles: StyleChain, ) { - let mut baseline = TextElem::baseline_in(styles); + let mut baseline = styles.resolve(TextElem::baseline); let mut compensation = Abs::zero(); - if let Some(scripts) = TextElem::shift_settings_in(styles) { - let font_metrics = TextElem::font_in(styles) + if let Some(scripts) = styles.get_ref(TextElem::shift_settings) { + let font_metrics = styles + .get_ref(TextElem::font) .into_iter() .find_map(|family| { world @@ -462,7 +464,7 @@ pub fn commit( if let Some(Item::Text(text)) = line.items.first() { if let Some(glyph) = text.glyphs.first() { if !text.dir.is_positive() - && TextElem::overhang_in(text.styles) + && text.styles.get(TextElem::overhang) && (line.items.len() > 1 || text.glyphs.len() > 1) { let amount = overhang(glyph.c) * glyph.x_advance.at(glyph.size); @@ -476,7 +478,7 @@ pub fn commit( if let Some(Item::Text(text)) = line.items.last() { if let Some(glyph) = text.glyphs.last() { if text.dir.is_positive() - && TextElem::overhang_in(text.styles) + && text.styles.get(TextElem::overhang) && (line.items.len() > 1 || text.glyphs.len() > 1) { let amount = overhang(glyph.c) * glyph.x_advance.at(glyph.size); diff --git a/crates/typst-layout/src/inline/linebreak.rs b/crates/typst-layout/src/inline/linebreak.rs index 709745edc..955360df1 100644 --- a/crates/typst-layout/src/inline/linebreak.rs +++ b/crates/typst-layout/src/inline/linebreak.rs @@ -846,7 +846,9 @@ fn hyphenate_at(p: &Preparation, offset: usize) -> bool { p.config.hyphenate.unwrap_or_else(|| { let (_, item) = p.get(offset); match item.text() { - Some(text) => TextElem::hyphenate_in(text.styles).unwrap_or(p.config.justify), + Some(text) => { + text.styles.get(TextElem::hyphenate).unwrap_or(p.config.justify) + } None => false, } }) @@ -857,7 +859,7 @@ fn lang_at(p: &Preparation, offset: usize) -> Option { let lang = p.config.lang.or_else(|| { let (_, item) = p.get(offset); let styles = item.text()?.styles; - Some(TextElem::lang_in(styles)) + Some(styles.get(TextElem::lang)) })?; let bytes = lang.as_str().as_bytes().try_into().ok()?; diff --git a/crates/typst-layout/src/inline/mod.rs b/crates/typst-layout/src/inline/mod.rs index 506fa5eac..06223cebf 100644 --- a/crates/typst-layout/src/inline/mod.rs +++ b/crates/typst-layout/src/inline/mod.rs @@ -14,7 +14,7 @@ pub use self::shaping::create_shape_plan; use comemo::{Track, Tracked, TrackedMut}; use typst_library::diag::SourceResult; use typst_library::engine::{Engine, Route, Sink, Traced}; -use typst_library::foundations::{Packed, Resolve, Smart, StyleChain}; +use typst_library::foundations::{Packed, Smart, StyleChain}; use typst_library::introspection::{Introspector, Locator, LocatorLink, SplitLocator}; use typst_library::layout::{Abs, AlignElem, Dir, FixedAlignment, Fragment, Size}; use typst_library::model::{ @@ -113,10 +113,10 @@ fn layout_par_impl( expand, Some(situation), &ConfigBase { - justify: elem.justify(styles), - linebreaks: elem.linebreaks(styles), - first_line_indent: elem.first_line_indent(styles), - hanging_indent: elem.hanging_indent(styles), + justify: elem.justify.get(styles), + linebreaks: elem.linebreaks.get(styles), + first_line_indent: elem.first_line_indent.get(styles), + hanging_indent: elem.hanging_indent.resolve(styles), }, ) } @@ -139,10 +139,10 @@ pub fn layout_inline<'a>( expand, None, &ConfigBase { - justify: ParElem::justify_in(shared), - linebreaks: ParElem::linebreaks_in(shared), - first_line_indent: ParElem::first_line_indent_in(shared), - hanging_indent: ParElem::hanging_indent_in(shared), + justify: shared.get(ParElem::justify), + linebreaks: shared.get(ParElem::linebreaks), + first_line_indent: shared.get(ParElem::first_line_indent), + hanging_indent: shared.resolve(ParElem::hanging_indent), }, ) } @@ -184,8 +184,8 @@ fn configuration( situation: Option, ) -> Config { let justify = base.justify; - let font_size = TextElem::size_in(shared); - let dir = TextElem::dir_in(shared); + let font_size = shared.resolve(TextElem::size); + let dir = shared.resolve(TextElem::dir); Config { justify, @@ -207,7 +207,7 @@ fn configuration( Some(ParSituation::Other) => all, None => false, } - && AlignElem::alignment_in(shared).resolve(shared).x == dir.start().into() + && shared.resolve(AlignElem::alignment).x == dir.start().into() { amount.at(font_size) } else { @@ -219,26 +219,26 @@ fn configuration( } else { Abs::zero() }, - numbering_marker: ParLine::numbering_in(shared).map(|numbering| { + numbering_marker: shared.get_cloned(ParLine::numbering).map(|numbering| { Packed::new(ParLineMarker::new( numbering, - ParLine::number_align_in(shared), - ParLine::number_margin_in(shared), + shared.get(ParLine::number_align), + shared.get(ParLine::number_margin), // Delay resolving the number clearance until line numbers are // laid out to avoid inconsistent spacing depending on varying // font size. - ParLine::number_clearance_in(shared), + shared.get(ParLine::number_clearance), )) }), - align: AlignElem::alignment_in(shared).fix(dir).x, + align: shared.get(AlignElem::alignment).fix(dir).x, font_size, dir, - hyphenate: shared_get(children, shared, TextElem::hyphenate_in) + hyphenate: shared_get(children, shared, |s| s.get(TextElem::hyphenate)) .map(|uniform| uniform.unwrap_or(justify)), - lang: shared_get(children, shared, TextElem::lang_in), - fallback: TextElem::fallback_in(shared), - cjk_latin_spacing: TextElem::cjk_latin_spacing_in(shared).is_auto(), - costs: TextElem::costs_in(shared), + lang: shared_get(children, shared, |s| s.get(TextElem::lang)), + fallback: shared.get(TextElem::fallback), + cjk_latin_spacing: shared.get(TextElem::cjk_latin_spacing).is_auto(), + costs: shared.get(TextElem::costs), } } @@ -314,7 +314,7 @@ fn shared_get( /// When we support some kind of more general ancestry mechanism, this can /// become more elegant. fn in_list(styles: StyleChain) -> bool { - ListElem::depth_in(styles).0 > 0 - || !EnumElem::parents_in(styles).is_empty() - || TermsElem::within_in(styles) + styles.get(ListElem::depth).0 > 0 + || !styles.get_cloned(EnumElem::parents).is_empty() + || styles.get(TermsElem::within) } diff --git a/crates/typst-layout/src/inline/shaping.rs b/crates/typst-layout/src/inline/shaping.rs index 48747cd50..d1e748da8 100644 --- a/crates/typst-layout/src/inline/shaping.rs +++ b/crates/typst-layout/src/inline/shaping.rs @@ -223,12 +223,12 @@ impl<'a> ShapedText<'a> { let mut frame = Frame::soft(size); frame.set_baseline(top); - let size = TextElem::size_in(self.styles); - let shift = TextElem::baseline_in(self.styles); - let decos = TextElem::deco_in(self.styles); - let fill = TextElem::fill_in(self.styles); - let stroke = TextElem::stroke_in(self.styles); - let span_offset = TextElem::span_offset_in(self.styles); + let size = self.styles.resolve(TextElem::size); + let shift = self.styles.resolve(TextElem::baseline); + let decos = self.styles.get_cloned(TextElem::deco); + let fill = self.styles.get_ref(TextElem::fill); + let stroke = self.styles.resolve(TextElem::stroke); + let span_offset = self.styles.get(TextElem::span_offset); for ((font, y_offset, glyph_size), group) in self .glyphs @@ -340,9 +340,9 @@ impl<'a> ShapedText<'a> { let mut top = Abs::zero(); let mut bottom = Abs::zero(); - let size = TextElem::size_in(self.styles); - let top_edge = TextElem::top_edge_in(self.styles); - let bottom_edge = TextElem::bottom_edge_in(self.styles); + let size = self.styles.resolve(TextElem::size); + let top_edge = self.styles.get(TextElem::top_edge); + let bottom_edge = self.styles.get(TextElem::bottom_edge); // Expand top and bottom by reading the font's vertical metrics. let mut expand = |font: &Font, bounds: TextEdgeBounds| { @@ -486,7 +486,7 @@ impl<'a> ShapedText<'a> { // that subtracting either of the endpoints by self.base doesn't // underflow. See . .unwrap_or_else(|| self.base..self.base); - let size = TextElem::size_in(self.styles); + let size = self.styles.resolve(TextElem::size); self.width += x_advance.at(size); let glyph = ShapedGlyph { font, @@ -603,9 +603,9 @@ pub fn shape_range<'a>( range: Range, styles: StyleChain<'a>, ) { - let script = TextElem::script_in(styles); - let lang = TextElem::lang_in(styles); - let region = TextElem::region_in(styles); + let script = styles.get(TextElem::script); + let lang = styles.get(TextElem::lang); + let region = styles.get(TextElem::region); let mut process = |range: Range, level: BidiLevel| { let dir = if level.is_ltr() { Dir::LTR } else { Dir::RTL }; let shaped = @@ -669,8 +669,8 @@ fn shape<'a>( lang: Lang, region: Option, ) -> ShapedText<'a> { - let size = TextElem::size_in(styles); - let shift_settings = TextElem::shift_settings_in(styles); + let size = styles.resolve(TextElem::size); + let shift_settings = styles.get(TextElem::shift_settings); let mut ctx = ShapingContext { engine, size, @@ -679,7 +679,7 @@ fn shape<'a>( styles, variant: variant(styles), features: features(styles), - fallback: TextElem::fallback_in(styles), + fallback: styles.get(TextElem::fallback), dir, shift_settings, }; @@ -783,7 +783,7 @@ fn shape_segment<'a>( let mut buffer = UnicodeBuffer::new(); buffer.push_str(text); buffer.set_language(language(ctx.styles)); - if let Some(script) = TextElem::script_in(ctx.styles).custom().and_then(|script| { + if let Some(script) = ctx.styles.get(TextElem::script).custom().and_then(|script| { rustybuzz::Script::from_iso15924_tag(Tag::from_bytes(script.as_bytes())) }) { buffer.set_script(script) @@ -1073,8 +1073,11 @@ fn shape_tofus(ctx: &mut ShapingContext, base: usize, text: &str, font: Font) { /// Apply tracking and spacing to the shaped glyphs. fn track_and_space(ctx: &mut ShapingContext) { - let tracking = Em::from_abs(TextElem::tracking_in(ctx.styles), ctx.size); - let spacing = TextElem::spacing_in(ctx.styles).map(|abs| Em::from_abs(abs, ctx.size)); + let tracking = Em::from_abs(ctx.styles.resolve(TextElem::tracking), ctx.size); + let spacing = ctx + .styles + .resolve(TextElem::spacing) + .map(|abs| Em::from_abs(abs, ctx.size)); let mut glyphs = ctx.glyphs.iter_mut().peekable(); while let Some(glyph) = glyphs.next() { diff --git a/crates/typst-layout/src/lists.rs b/crates/typst-layout/src/lists.rs index 974788a70..adb793fb9 100644 --- a/crates/typst-layout/src/lists.rs +++ b/crates/typst-layout/src/lists.rs @@ -20,20 +20,21 @@ pub fn layout_list( styles: StyleChain, regions: Regions, ) -> SourceResult { - let indent = elem.indent(styles); - let body_indent = elem.body_indent(styles); - let tight = elem.tight(styles); - let gutter = elem.spacing(styles).unwrap_or_else(|| { + let indent = elem.indent.get(styles); + let body_indent = elem.body_indent.get(styles); + let tight = elem.tight.get(styles); + let gutter = elem.spacing.get(styles).unwrap_or_else(|| { if tight { - ParElem::leading_in(styles).into() + styles.get(ParElem::leading) } else { - ParElem::spacing_in(styles).into() + styles.get(ParElem::spacing) } }); - let Depth(depth) = ListElem::depth_in(styles); + let Depth(depth) = styles.get(ListElem::depth); let marker = elem - .marker(styles) + .marker + .get_ref(styles) .resolve(engine, styles, depth)? // avoid '#set align' interference with the list .aligned(HAlignment::Start + VAlignment::Top); @@ -52,7 +53,7 @@ pub fn layout_list( cells.push(Cell::new(marker.clone(), locator.next(&marker.span()))); cells.push(Cell::new(Content::empty(), locator.next(&()))); cells.push(Cell::new( - body.styled(ListElem::set_depth(Depth(1))), + body.set(ListElem::depth, Depth(1)), locator.next(&item.body.span()), )); } @@ -81,40 +82,40 @@ pub fn layout_enum( styles: StyleChain, regions: Regions, ) -> SourceResult { - let numbering = elem.numbering(styles); - let reversed = elem.reversed(styles); - let indent = elem.indent(styles); - let body_indent = elem.body_indent(styles); - let tight = elem.tight(styles); - let gutter = elem.spacing(styles).unwrap_or_else(|| { + let numbering = elem.numbering.get_ref(styles); + let reversed = elem.reversed.get(styles); + let indent = elem.indent.get(styles); + let body_indent = elem.body_indent.get(styles); + let tight = elem.tight.get(styles); + let gutter = elem.spacing.get(styles).unwrap_or_else(|| { if tight { - ParElem::leading_in(styles).into() + styles.get(ParElem::leading) } else { - ParElem::spacing_in(styles).into() + styles.get(ParElem::spacing) } }); let mut cells = vec![]; let mut locator = locator.split(); - let mut number = elem.start(styles).unwrap_or_else(|| { + let mut number = elem.start.get(styles).unwrap_or_else(|| { if reversed { elem.children.len() as u64 } else { 1 } }); - let mut parents = EnumElem::parents_in(styles); + let mut parents = styles.get_cloned(EnumElem::parents); - let full = elem.full(styles); + let full = elem.full.get(styles); // Horizontally align based on the given respective parameter. // Vertically align to the top to avoid inheriting `horizon` or `bottom` // alignment from the context and having the number be displaced in // relation to the item it refers to. - let number_align = elem.number_align(styles); + let number_align = elem.number_align.get(styles); for item in &elem.children { - number = item.number(styles).unwrap_or(number); + number = item.number.get(styles).unwrap_or(number); let context = Context::new(None, Some(styles)); let resolved = if full { @@ -133,8 +134,7 @@ pub fn layout_enum( // Disable overhang as a workaround to end-aligned dots glitching // and decreasing spacing between numbers and items. - let resolved = - resolved.aligned(number_align).styled(TextElem::set_overhang(false)); + let resolved = resolved.aligned(number_align).set(TextElem::overhang, false); // Text in wide enums shall always turn into paragraphs. let mut body = item.body.clone(); @@ -146,7 +146,7 @@ pub fn layout_enum( cells.push(Cell::new(resolved, locator.next(&()))); cells.push(Cell::new(Content::empty(), locator.next(&()))); cells.push(Cell::new( - body.styled(EnumElem::set_parents(smallvec![number])), + body.set(EnumElem::parents, smallvec![number]), locator.next(&item.body.span()), )); number = diff --git a/crates/typst-layout/src/math/accent.rs b/crates/typst-layout/src/math/accent.rs index 159703b8e..e7f051ace 100644 --- a/crates/typst-layout/src/math/accent.rs +++ b/crates/typst-layout/src/math/accent.rs @@ -24,7 +24,7 @@ pub fn layout_accent( // Try to replace the base glyph with its dotless variant. let dtls = style_dtls(); let base_styles = - if top_accent && elem.dotless(styles) { styles.chain(&dtls) } else { styles }; + if top_accent && elem.dotless.get(styles) { styles.chain(&dtls) } else { styles }; let cramped = style_cramped(); let base = ctx.layout_into_fragment(&elem.base, base_styles.chain(&cramped))?; @@ -47,7 +47,7 @@ pub fn layout_accent( // Forcing the accent to be at least as large as the base makes it too wide // in many cases. - let width = elem.size(styles).relative_to(base.width()); + let width = elem.size.resolve(styles).relative_to(base.width()); let short_fall = ACCENT_SHORT_FALL.at(glyph.item.size); glyph.stretch_horizontal(ctx, width - short_fall); let accent_attach = glyph.accent_attach.0; diff --git a/crates/typst-layout/src/math/attach.rs b/crates/typst-layout/src/math/attach.rs index a7f3cad5f..78b6f5515 100644 --- a/crates/typst-layout/src/math/attach.rs +++ b/crates/typst-layout/src/math/attach.rs @@ -31,16 +31,16 @@ pub fn layout_attach( let mut base = ctx.layout_into_fragment(&elem.base, styles)?; let sup_style = style_for_superscript(styles); let sup_style_chain = styles.chain(&sup_style); - let tl = elem.tl(sup_style_chain); - let tr = elem.tr(sup_style_chain); + let tl = elem.tl.get_cloned(sup_style_chain); + let tr = elem.tr.get_cloned(sup_style_chain); let primed = tr.as_ref().is_some_and(|content| content.is::()); - let t = elem.t(sup_style_chain); + let t = elem.t.get_cloned(sup_style_chain); let sub_style = style_for_subscript(styles); let sub_style_chain = styles.chain(&sub_style); - let bl = elem.bl(sub_style_chain); - let br = elem.br(sub_style_chain); - let b = elem.b(sub_style_chain); + let bl = elem.bl.get_cloned(sub_style_chain); + let br = elem.br.get_cloned(sub_style_chain); + let b = elem.b.get_cloned(sub_style_chain); let limits = base.limits().active(styles); let (t, tr) = match (t, tr) { @@ -146,7 +146,7 @@ pub fn layout_limits( ctx: &mut MathContext, styles: StyleChain, ) -> SourceResult<()> { - let limits = if elem.inline(styles) { Limits::Always } else { Limits::Display }; + let limits = if elem.inline.get(styles) { Limits::Always } else { Limits::Display }; let mut fragment = ctx.layout_into_fragment(&elem.body, styles)?; fragment.set_limits(limits); ctx.push(fragment); @@ -161,7 +161,8 @@ fn stretch_size(styles: StyleChain, elem: &Packed) -> Option().map(|stretch| stretch.size(styles)) + base.to_packed::() + .map(|stretch| stretch.size.resolve(styles)) } /// Lay out the attachments. @@ -397,7 +398,7 @@ fn compute_script_shifts( base: &MathFragment, [tl, tr, bl, br]: [&Option; 4], ) -> (Abs, Abs) { - let sup_shift_up = if EquationElem::cramped_in(styles) { + let sup_shift_up = if styles.get(EquationElem::cramped) { scaled!(ctx, styles, superscript_shift_up_cramped) } else { scaled!(ctx, styles, superscript_shift_up) diff --git a/crates/typst-layout/src/math/cancel.rs b/crates/typst-layout/src/math/cancel.rs index 9826397fa..57a32ca2a 100644 --- a/crates/typst-layout/src/math/cancel.rs +++ b/crates/typst-layout/src/math/cancel.rs @@ -27,16 +27,16 @@ pub fn layout_cancel( let mut body = body.into_frame(); let body_size = body.size(); let span = elem.span(); - let length = elem.length(styles); + let length = elem.length.resolve(styles); - let stroke = elem.stroke(styles).unwrap_or(FixedStroke { - paint: TextElem::fill_in(styles).as_decoration(), + let stroke = elem.stroke.resolve(styles).unwrap_or(FixedStroke { + paint: styles.get_ref(TextElem::fill).as_decoration(), ..Default::default() }); - let invert = elem.inverted(styles); - let cross = elem.cross(styles); - let angle = elem.angle(styles); + let invert = elem.inverted.get(styles); + let cross = elem.cross.get(styles); + let angle = elem.angle.get_ref(styles); let invert_first_line = !cross && invert; let first_line = draw_cancel_line( @@ -44,7 +44,7 @@ pub fn layout_cancel( length, stroke.clone(), invert_first_line, - &angle, + angle, body_size, styles, span, @@ -57,7 +57,7 @@ pub fn layout_cancel( if cross { // Draw the second line. let second_line = - draw_cancel_line(ctx, length, stroke, true, &angle, body_size, styles, span)?; + draw_cancel_line(ctx, length, stroke, true, angle, body_size, styles, span)?; body.push_frame(center, second_line); } diff --git a/crates/typst-layout/src/math/frac.rs b/crates/typst-layout/src/math/frac.rs index 091f328f6..12a2c6fd1 100644 --- a/crates/typst-layout/src/math/frac.rs +++ b/crates/typst-layout/src/math/frac.rs @@ -124,7 +124,7 @@ fn layout_frac_like( FrameItem::Shape( Geometry::Line(Point::with_x(line_width)).stroked( FixedStroke::from_pair( - TextElem::fill_in(styles).as_decoration(), + styles.get_ref(TextElem::fill).as_decoration(), thickness, ), ), diff --git a/crates/typst-layout/src/math/fragment.rs b/crates/typst-layout/src/math/fragment.rs index c5891f7d5..758dd401f 100644 --- a/crates/typst-layout/src/math/fragment.rs +++ b/crates/typst-layout/src/math/fragment.rs @@ -315,7 +315,8 @@ impl GlyphFragment { let cluster = info.cluster as usize; let c = text[cluster..].chars().next().unwrap(); let limits = Limits::for_char(c); - let class = EquationElem::class_in(styles) + let class = styles + .get(EquationElem::class) .or_else(|| default_math_class(c)) .unwrap_or(MathClass::Normal); @@ -331,11 +332,11 @@ impl GlyphFragment { let item = TextItem { font: font.clone(), - size: TextElem::size_in(styles), - fill: TextElem::fill_in(styles).as_decoration(), - stroke: TextElem::stroke_in(styles).map(|s| s.unwrap_or_default()), - lang: TextElem::lang_in(styles), - region: TextElem::region_in(styles), + size: styles.resolve(TextElem::size), + fill: styles.get_ref(TextElem::fill).as_decoration(), + stroke: styles.resolve(TextElem::stroke).map(|s| s.unwrap_or_default()), + lang: styles.get(TextElem::lang), + region: styles.get(TextElem::region), text: text.into(), glyphs: vec![glyph.clone()], }; @@ -344,7 +345,7 @@ impl GlyphFragment { item, base_glyph: glyph, // Math - math_size: EquationElem::size_in(styles), + math_size: styles.get(EquationElem::size), class, limits, mid_stretched: None, @@ -356,7 +357,7 @@ impl GlyphFragment { baseline: None, // Misc align: Abs::zero(), - shift: TextElem::baseline_in(styles), + shift: styles.resolve(TextElem::baseline), modifiers: FrameModifiers::get_in(styles), }; fragment.update_glyph(); @@ -541,9 +542,9 @@ impl FrameFragment { let accent_attach = frame.width() / 2.0; Self { frame: frame.modified(&FrameModifiers::get_in(styles)), - font_size: TextElem::size_in(styles), - class: EquationElem::class_in(styles).unwrap_or(MathClass::Normal), - math_size: EquationElem::size_in(styles), + font_size: styles.resolve(TextElem::size), + class: styles.get(EquationElem::class).unwrap_or(MathClass::Normal), + math_size: styles.get(EquationElem::size), limits: Limits::Never, spaced: false, base_ascent, @@ -864,7 +865,7 @@ impl Limits { pub fn active(&self, styles: StyleChain) -> bool { match self { Self::Always => true, - Self::Display => EquationElem::size_in(styles) == MathSize::Display, + Self::Display => styles.get(EquationElem::size) == MathSize::Display, Self::Never => false, } } diff --git a/crates/typst-layout/src/math/lr.rs b/crates/typst-layout/src/math/lr.rs index a3b5cb05c..2348025e8 100644 --- a/crates/typst-layout/src/math/lr.rs +++ b/crates/typst-layout/src/math/lr.rs @@ -22,7 +22,7 @@ pub fn layout_lr( // Extract implicit LrElem. if let Some(lr) = body.to_packed::() { - if lr.size(styles).is_one() { + if lr.size.get(styles).is_one() { body = &lr.body; } } @@ -41,7 +41,7 @@ pub fn layout_lr( .unwrap_or_default(); let relative_to = 2.0 * max_extent; - let height = elem.size(styles); + let height = elem.size.resolve(styles); // Scale up fragments at both ends. match inner_fragments { diff --git a/crates/typst-layout/src/math/mat.rs b/crates/typst-layout/src/math/mat.rs index 278b1343e..4a897a03e 100644 --- a/crates/typst-layout/src/math/mat.rs +++ b/crates/typst-layout/src/math/mat.rs @@ -30,15 +30,15 @@ pub fn layout_vec( ctx, styles, &[column], - elem.align(styles), + elem.align.resolve(styles), LeftRightAlternator::Right, None, - Axes::with_y(elem.gap(styles)), + Axes::with_y(elem.gap.resolve(styles)), span, "elements", )?; - let delim = elem.delim(styles); + let delim = elem.delim.get(styles); layout_delimiters(ctx, styles, frame, delim.open(), delim.close(), span) } @@ -59,14 +59,17 @@ pub fn layout_cases( FixedAlignment::Start, LeftRightAlternator::None, None, - Axes::with_y(elem.gap(styles)), + Axes::with_y(elem.gap.resolve(styles)), span, "branches", )?; - let delim = elem.delim(styles); - let (open, close) = - if elem.reverse(styles) { (None, delim.close()) } else { (delim.open(), None) }; + let delim = elem.delim.get(styles); + let (open, close) = if elem.reverse.get(styles) { + (None, delim.close()) + } else { + (delim.open(), None) + }; layout_delimiters(ctx, styles, frame, open, close, span) } @@ -81,7 +84,7 @@ pub fn layout_mat( let rows = &elem.rows; let ncols = rows.first().map_or(0, |row| row.len()); - let augment = elem.augment(styles); + let augment = elem.augment.resolve(styles); if let Some(aug) = &augment { for &offset in &aug.hline.0 { if offset == 0 || offset.unsigned_abs() >= rows.len() { @@ -116,15 +119,15 @@ pub fn layout_mat( ctx, styles, &columns, - elem.align(styles), + elem.align.resolve(styles), LeftRightAlternator::Right, augment, - Axes::new(elem.column_gap(styles), elem.row_gap(styles)), + Axes::new(elem.column_gap.resolve(styles), elem.row_gap.resolve(styles)), span, "cells", )?; - let delim = elem.delim(styles); + let delim = elem.delim.get(styles); layout_delimiters(ctx, styles, frame, delim.open(), delim.close(), span) } @@ -157,7 +160,7 @@ fn layout_body( let default_stroke_thickness = DEFAULT_STROKE_THICKNESS.resolve(styles); let default_stroke = FixedStroke { thickness: default_stroke_thickness, - paint: TextElem::fill_in(styles).as_decoration(), + paint: styles.get_ref(TextElem::fill).as_decoration(), cap: LineCap::Square, ..Default::default() }; diff --git a/crates/typst-layout/src/math/mod.rs b/crates/typst-layout/src/math/mod.rs index 5fd22e578..390835067 100644 --- a/crates/typst-layout/src/math/mod.rs +++ b/crates/typst-layout/src/math/mod.rs @@ -51,7 +51,7 @@ pub fn layout_equation_inline( styles: StyleChain, region: Size, ) -> SourceResult> { - assert!(!elem.block(styles)); + assert!(!elem.block.get(styles)); let font = find_math_font(engine, styles, elem.span())?; @@ -78,12 +78,12 @@ pub fn layout_equation_inline( for item in &mut items { let InlineItem::Frame(frame) = item else { continue }; - let slack = ParElem::leading_in(styles) * 0.7; + let slack = styles.resolve(ParElem::leading) * 0.7; let (t, b) = font.edges( - TextElem::top_edge_in(styles), - TextElem::bottom_edge_in(styles), - TextElem::size_in(styles), + styles.get(TextElem::top_edge), + styles.get(TextElem::bottom_edge), + styles.resolve(TextElem::size), TextEdgeBounds::Frame(frame), ); @@ -105,7 +105,7 @@ pub fn layout_equation_block( styles: StyleChain, regions: Regions, ) -> SourceResult { - assert!(elem.block(styles)); + assert!(elem.block.get(styles)); let span = elem.span(); let font = find_math_font(engine, styles, span)?; @@ -121,7 +121,7 @@ pub fn layout_equation_block( .multiline_frame_builder(styles); let width = full_equation_builder.size.x; - let equation_builders = if BlockElem::breakable_in(styles) { + let equation_builders = if styles.get(BlockElem::breakable) { let mut rows = full_equation_builder.frames.into_iter().peekable(); let mut equation_builders = vec![]; let mut last_first_pos = Point::zero(); @@ -188,7 +188,7 @@ pub fn layout_equation_block( vec![full_equation_builder] }; - let Some(numbering) = (**elem).numbering(styles) else { + let Some(numbering) = elem.numbering.get_ref(styles) else { let frames = equation_builders .into_iter() .map(MathRunFrameBuilder::build) @@ -197,7 +197,7 @@ pub fn layout_equation_block( }; let pod = Region::new(regions.base(), Axes::splat(false)); - let counter = Counter::of(EquationElem::elem()) + let counter = Counter::of(EquationElem::ELEM) .display_at_loc(engine, elem.location().unwrap(), styles, numbering)? .spanned(span); let number = crate::layout_frame(engine, &counter, locator.next(&()), styles, pod)?; @@ -205,7 +205,7 @@ pub fn layout_equation_block( static NUMBER_GUTTER: Em = Em::new(0.5); let full_number_width = number.width() + NUMBER_GUTTER.resolve(styles); - let number_align = match elem.number_align(styles) { + let number_align = match elem.number_align.get(styles) { SpecificAlignment::H(h) => SpecificAlignment::Both(h, VAlignment::Horizon), SpecificAlignment::V(v) => SpecificAlignment::Both(OuterHAlignment::End, v), SpecificAlignment::Both(h, v) => SpecificAlignment::Both(h, v), @@ -224,7 +224,7 @@ pub fn layout_equation_block( builder, number.clone(), number_align.resolve(styles), - AlignElem::alignment_in(styles).resolve(styles).x, + styles.get(AlignElem::alignment).resolve(styles).x, regions.size.x, full_number_width, ) @@ -472,7 +472,9 @@ impl<'a, 'v, 'e> MathContext<'a, 'v, 'e> { let outer = styles; for (elem, styles) in pairs { // Hack because the font is fixed in math. - if styles != outer && TextElem::font_in(styles) != TextElem::font_in(outer) { + if styles != outer + && styles.get_ref(TextElem::font) != outer.get_ref(TextElem::font) + { let frame = layout_external(elem, self, styles)?; self.push(FrameFragment::new(styles, frame).with_spaced(true)); continue; @@ -603,7 +605,10 @@ fn layout_h( ) -> SourceResult<()> { if let Spacing::Rel(rel) = elem.amount { if rel.rel.is_zero() { - ctx.push(MathFragment::Spacing(rel.abs.resolve(styles), elem.weak(styles))); + ctx.push(MathFragment::Spacing( + rel.abs.resolve(styles), + elem.weak.get(styles), + )); } } Ok(()) @@ -616,7 +621,7 @@ fn layout_class( ctx: &mut MathContext, styles: StyleChain, ) -> SourceResult<()> { - let style = EquationElem::set_class(Some(elem.class)).wrap(); + let style = EquationElem::class.set(Some(elem.class)).wrap(); let mut fragment = ctx.layout_into_fragment(&elem.body, styles.chain(&style))?; fragment.set_class(elem.class); fragment.set_limits(Limits::for_class(elem.class)); @@ -642,7 +647,7 @@ fn layout_op( .with_italics_correction(italics) .with_accent_attach(accent_attach) .with_text_like(text_like) - .with_limits(if elem.limits(styles) { + .with_limits(if elem.limits.get(styles) { Limits::Display } else { Limits::Never diff --git a/crates/typst-layout/src/math/root.rs b/crates/typst-layout/src/math/root.rs index 91b9b16af..30948e08e 100644 --- a/crates/typst-layout/src/math/root.rs +++ b/crates/typst-layout/src/math/root.rs @@ -17,7 +17,7 @@ pub fn layout_root( ctx: &mut MathContext, styles: StyleChain, ) -> SourceResult<()> { - let index = elem.index(styles); + let index = elem.index.get_ref(styles); let span = elem.span(); let gap = scaled!( @@ -54,7 +54,7 @@ pub fn layout_root( let sqrt = sqrt.into_frame(); // Layout the index. - let sscript = EquationElem::set_size(MathSize::ScriptScript).wrap(); + let sscript = EquationElem::size.set(MathSize::ScriptScript).wrap(); let index = index .as_ref() .map(|elem| ctx.layout_into_frame(elem, styles.chain(&sscript))) @@ -112,7 +112,7 @@ pub fn layout_root( FrameItem::Shape( Geometry::Line(Point::with_x(radicand.width())).stroked( FixedStroke::from_pair( - TextElem::fill_in(styles).as_decoration(), + styles.get_ref(TextElem::fill).as_decoration(), thickness, ), ), diff --git a/crates/typst-layout/src/math/run.rs b/crates/typst-layout/src/math/run.rs index 4ec76c253..161fa1062 100644 --- a/crates/typst-layout/src/math/run.rs +++ b/crates/typst-layout/src/math/run.rs @@ -194,13 +194,13 @@ impl MathRun { let row_count = rows.len(); let alignments = alignments(&rows); - let leading = if EquationElem::size_in(styles) >= MathSize::Text { - ParElem::leading_in(styles) + let leading = if styles.get(EquationElem::size) >= MathSize::Text { + styles.resolve(ParElem::leading) } else { TIGHT_LEADING.resolve(styles) }; - let align = AlignElem::alignment_in(styles).resolve(styles).x; + let align = styles.resolve(AlignElem::alignment).x; let mut frames: Vec<(Frame, Point)> = vec![]; let mut size = Size::zero(); for (i, row) in rows.into_iter().enumerate() { diff --git a/crates/typst-layout/src/math/shared.rs b/crates/typst-layout/src/math/shared.rs index 1f88d2dd7..c9d20aa68 100644 --- a/crates/typst-layout/src/math/shared.rs +++ b/crates/typst-layout/src/math/shared.rs @@ -10,7 +10,7 @@ use super::{LeftRightAlternator, MathContext, MathFragment, MathRun}; macro_rules! scaled { ($ctx:expr, $styles:expr, text: $text:ident, display: $display:ident $(,)?) => { - match typst_library::math::EquationElem::size_in($styles) { + match $styles.get(typst_library::math::EquationElem::size) { typst_library::math::MathSize::Display => scaled!($ctx, $styles, $display), _ => scaled!($ctx, $styles, $text), } @@ -19,7 +19,7 @@ macro_rules! scaled { $crate::math::Scaled::scaled( $ctx.constants.$name(), $ctx, - typst_library::text::TextElem::size_in($styles), + $styles.resolve(typst_library::text::TextElem::size), ) }; } @@ -58,55 +58,62 @@ impl Scaled for MathValue<'_> { /// Styles something as cramped. pub fn style_cramped() -> LazyHash