summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-03-08 13:02:41 +0100
committerLaurenz <laurmaedje@gmail.com>2023-03-08 14:35:33 +0100
commitd7a65fa26d131179d9d82226e5ee1b562084e48a (patch)
treec21ab20e9fb851e14e1ebea3e14fc351b1fdbcc9
parente5eab73374880077971f3f22acbdd3d302877128 (diff)
Rework style chain access
-rw-r--r--docs/src/lib.rs8
-rw-r--r--library/src/layout/align.rs3
-rw-r--r--library/src/layout/columns.rs4
-rw-r--r--library/src/layout/container.rs38
-rw-r--r--library/src/layout/enum.rs19
-rw-r--r--library/src/layout/flow.rs12
-rw-r--r--library/src/layout/grid.rs2
-rw-r--r--library/src/layout/hide.rs2
-rw-r--r--library/src/layout/list.rs17
-rw-r--r--library/src/layout/mod.rs6
-rw-r--r--library/src/layout/page.rs24
-rw-r--r--library/src/layout/par.rs55
-rw-r--r--library/src/layout/repeat.rs2
-rw-r--r--library/src/layout/stack.rs7
-rw-r--r--library/src/layout/table.rs10
-rw-r--r--library/src/layout/terms.rs11
-rw-r--r--library/src/layout/transform.rs4
-rw-r--r--library/src/lib.rs6
-rw-r--r--library/src/math/ctx.rs23
-rw-r--r--library/src/math/frac.rs2
-rw-r--r--library/src/math/fragment.rs4
-rw-r--r--library/src/math/matrix.rs6
-rw-r--r--library/src/math/mod.rs21
-rw-r--r--library/src/math/root.rs2
-rw-r--r--library/src/math/row.rs4
-rw-r--r--library/src/meta/document.rs4
-rw-r--r--library/src/meta/heading.rs16
-rw-r--r--library/src/meta/link.rs4
-rw-r--r--library/src/meta/outline.rs14
-rw-r--r--library/src/shared/ext.rs36
-rw-r--r--library/src/text/deco.rs51
-rw-r--r--library/src/text/misc.rs8
-rw-r--r--library/src/text/mod.rs13
-rw-r--r--library/src/text/raw.rs16
-rw-r--r--library/src/text/shaping.rs69
-rw-r--r--library/src/text/shift.rs18
-rw-r--r--library/src/visualize/image.rs2
-rw-r--r--library/src/visualize/line.rs2
-rw-r--r--library/src/visualize/shape.rs36
-rw-r--r--macros/src/node.rs356
-rw-r--r--src/doc.rs2
-rw-r--r--src/eval/mod.rs4
-rw-r--r--src/model/content.rs26
-rw-r--r--src/model/styles.rs252
-rw-r--r--tests/src/tests.rs10
45 files changed, 533 insertions, 698 deletions
diff --git a/docs/src/lib.rs b/docs/src/lib.rs
index 7ddc1c70..563d565c 100644
--- a/docs/src/lib.rs
+++ b/docs/src/lib.rs
@@ -39,10 +39,12 @@ static FONTS: Lazy<(Prehashed<FontBook>, Vec<Font>)> = Lazy::new(|| {
static LIBRARY: Lazy<Prehashed<Library>> = Lazy::new(|| {
let mut lib = typst_library::build();
- lib.styles.set(PageNode::WIDTH, Smart::Custom(Abs::pt(240.0).into()));
- lib.styles.set(PageNode::HEIGHT, Smart::Auto);
lib.styles
- .set(PageNode::MARGIN, Sides::splat(Some(Smart::Custom(Abs::pt(15.0).into()))));
+ .set(PageNode::set_width(Smart::Custom(Abs::pt(240.0).into())));
+ lib.styles.set(PageNode::set_height(Smart::Auto));
+ lib.styles.set(PageNode::set_margin(Sides::splat(Some(Smart::Custom(
+ Abs::pt(15.0).into(),
+ )))));
typst::eval::set_lang_items(lib.items.clone());
Prehashed::new(lib)
});
diff --git a/library/src/layout/align.rs b/library/src/layout/align.rs
index e0df0436..fdd4d0ea 100644
--- a/library/src/layout/align.rs
+++ b/library/src/layout/align.rs
@@ -16,8 +16,7 @@ use crate::prelude::*;
/// Category: layout
#[node(Show)]
#[set({
- let aligns: Axes<Option<GenAlign>> = args.find()?.unwrap_or_default();
- styles.set(Self::ALIGNMENT, aligns);
+ styles.set(Self::set_alignment(args.find()?.unwrap_or_default()));
})]
pub struct AlignNode {
/// The alignment along both axes.
diff --git a/library/src/layout/columns.rs b/library/src/layout/columns.rs
index 94c04509..27339628 100644
--- a/library/src/layout/columns.rs
+++ b/library/src/layout/columns.rs
@@ -68,7 +68,7 @@ impl Layout for ColumnsNode {
// Determine the width of the gutter and each column.
let columns = self.count().get();
- let gutter = styles.get(Self::GUTTER).relative_to(regions.base().x);
+ let gutter = Self::gutter_in(styles).relative_to(regions.base().x);
let width = (regions.size.x - gutter * (columns - 1) as f64) / columns as f64;
let backlog: Vec<_> = std::iter::once(&regions.size.y)
@@ -90,7 +90,7 @@ impl Layout for ColumnsNode {
let mut frames = body.layout(vt, styles, pod)?.into_iter();
let mut finished = vec![];
- let dir = styles.get(TextNode::DIR);
+ let dir = TextNode::dir_in(styles);
let total_regions = (frames.len() as f32 / columns as f32).ceil() as usize;
// Stitch together the columns for each region.
diff --git a/library/src/layout/container.rs b/library/src/layout/container.rs
index 8f5d69cc..bdce1922 100644
--- a/library/src/layout/container.rs
+++ b/library/src/layout/container.rs
@@ -134,7 +134,7 @@ impl Layout for BoxNode {
// Apply inset.
let mut body = self.body().unwrap_or_default();
- let inset = styles.get(Self::INSET);
+ let inset = Self::inset_in(styles);
if inset.iter().any(|v| !v.is_zero()) {
body = body.padded(inset.map(|side| side.map(Length::from)));
}
@@ -145,21 +145,20 @@ impl Layout for BoxNode {
let mut frame = body.layout(vt, styles, pod)?.into_frame();
// Apply baseline shift.
- let shift = styles.get(Self::BASELINE).relative_to(frame.height());
+ let shift = Self::baseline_in(styles).relative_to(frame.height());
if !shift.is_zero() {
frame.set_baseline(frame.baseline() - shift);
}
// Prepare fill and stroke.
- let fill = styles.get(Self::FILL);
- let stroke = styles
- .get(Self::STROKE)
- .map(|s| s.map(PartialStroke::unwrap_or_default));
+ let fill = Self::fill_in(styles);
+ let stroke =
+ Self::stroke_in(styles).map(|s| s.map(PartialStroke::unwrap_or_default));
// Add fill and/or stroke.
if fill.is_some() || stroke.iter().any(Option::is_some) {
- let outset = styles.get(Self::OUTSET);
- let radius = styles.get(Self::RADIUS);
+ let outset = Self::outset_in(styles);
+ let radius = Self::radius_in(styles);
frame.fill_and_stroke(fill, stroke, outset, radius);
}
@@ -220,16 +219,16 @@ impl Layout for BoxNode {
#[set({
let spacing = args.named("spacing")?;
styles.set_opt(
- Self::ABOVE,
args.named("above")?
.map(VNode::block_around)
- .or_else(|| spacing.map(VNode::block_spacing)),
+ .or_else(|| spacing.map(VNode::block_spacing))
+ .map(Self::set_above),
);
styles.set_opt(
- Self::BELOW,
args.named("below")?
.map(VNode::block_around)
- .or_else(|| spacing.map(VNode::block_spacing)),
+ .or_else(|| spacing.map(VNode::block_spacing))
+ .map(Self::set_below),
);
})]
pub struct BlockNode {
@@ -361,7 +360,7 @@ impl Layout for BlockNode {
) -> SourceResult<Fragment> {
// Apply inset.
let mut body = self.body().unwrap_or_default();
- let inset = styles.get(Self::INSET);
+ let inset = Self::inset_in(styles);
if inset.iter().any(|v| !v.is_zero()) {
body = body.clone().padded(inset.map(|side| side.map(Length::from)));
}
@@ -376,7 +375,7 @@ impl Layout for BlockNode {
.unwrap_or(regions.base());
// Layout the child.
- let mut frames = if styles.get(Self::BREAKABLE) {
+ let mut frames = if Self::breakable_in(styles) {
// Measure to ensure frames for all regions have the same width.
if sizing.x == Smart::Auto {
let pod = Regions::one(size, Axes::splat(false));
@@ -414,10 +413,9 @@ impl Layout for BlockNode {
};
// Prepare fill and stroke.
- let fill = styles.get(Self::FILL);
- let stroke = styles
- .get(Self::STROKE)
- .map(|s| s.map(PartialStroke::unwrap_or_default));
+ let fill = Self::fill_in(styles);
+ let stroke =
+ Self::stroke_in(styles).map(|s| s.map(PartialStroke::unwrap_or_default));
// Add fill and/or stroke.
if fill.is_some() || stroke.iter().any(Option::is_some) {
@@ -426,8 +424,8 @@ impl Layout for BlockNode {
skip = first.is_empty() && rest.iter().any(|frame| !frame.is_empty());
}
- let outset = styles.get(Self::OUTSET);
- let radius = styles.get(Self::RADIUS);
+ let outset = Self::outset_in(styles);
+ let radius = Self::radius_in(styles);
for frame in frames.iter_mut().skip(skip as usize) {
frame.fill_and_stroke(fill, stroke, outset, radius);
}
diff --git a/library/src/layout/enum.rs b/library/src/layout/enum.rs
index 853a7a67..4876b616 100644
--- a/library/src/layout/enum.rs
+++ b/library/src/layout/enum.rs
@@ -187,21 +187,20 @@ impl Layout for EnumNode {
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
- let numbering = styles.get(Self::NUMBERING);
- let indent = styles.get(Self::INDENT);
- let body_indent = styles.get(Self::BODY_INDENT);
+ let numbering = Self::numbering_in(styles);
+ let indent = Self::indent_in(styles);
+ let body_indent = Self::body_indent_in(styles);
let gutter = if self.tight() {
- styles.get(ParNode::LEADING).into()
+ ParNode::leading_in(styles).into()
} else {
- styles
- .get(Self::SPACING)
- .unwrap_or_else(|| styles.get(BlockNode::BELOW).amount())
+ Self::spacing_in(styles)
+ .unwrap_or_else(|| BlockNode::below_in(styles).amount())
};
let mut cells = vec![];
let mut number = NonZeroUsize::new(1).unwrap();
- let mut parents = styles.get(Self::PARENTS);
- let full = styles.get(Self::FULL);
+ let mut parents = Self::parents_in(styles);
+ let full = Self::full_in(styles);
for item in self.children() {
number = item.number().unwrap_or(number);
@@ -223,7 +222,7 @@ impl Layout for EnumNode {
cells.push(Content::empty());
cells.push(resolved);
cells.push(Content::empty());
- cells.push(item.body().styled(Self::PARENTS, Parent(number)));
+ cells.push(item.body().styled(Self::set_parents(Parent(number))));
number = number.saturating_add(1);
}
diff --git a/library/src/layout/flow.rs b/library/src/layout/flow.rs
index 8554bd98..5d679570 100644
--- a/library/src/layout/flow.rs
+++ b/library/src/layout/flow.rs
@@ -134,8 +134,8 @@ impl<'a> FlowLayouter<'a> {
par: &ParNode,
styles: StyleChain,
) -> SourceResult<()> {
- let aligns = styles.get(AlignNode::ALIGNMENT).resolve(styles);
- let leading = styles.get(ParNode::LEADING);
+ let aligns = AlignNode::alignment_in(styles).resolve(styles);
+ let leading = ParNode::leading_in(styles);
let consecutive = self.last_was_par;
let frames = par
.layout(vt, styles, consecutive, self.regions.base(), self.regions.expand.x)?
@@ -180,8 +180,8 @@ impl<'a> FlowLayouter<'a> {
content: &Content,
styles: StyleChain,
) -> SourceResult<()> {
- let aligns = styles.get(AlignNode::ALIGNMENT).resolve(styles);
- let sticky = styles.get(BlockNode::STICKY);
+ let aligns = AlignNode::alignment_in(styles).resolve(styles);
+ let sticky = BlockNode::sticky_in(styles);
let pod = Regions::one(self.regions.base(), Axes::splat(false));
let layoutable = content.with::<dyn Layout>().unwrap();
let frame = layoutable.layout(vt, styles, pod)?.into_frame();
@@ -208,10 +208,10 @@ impl<'a> FlowLayouter<'a> {
}
// How to align the block.
- let aligns = styles.get(AlignNode::ALIGNMENT).resolve(styles);
+ let aligns = AlignNode::alignment_in(styles).resolve(styles);
// Layout the block itself.
- let sticky = styles.get(BlockNode::STICKY);
+ let sticky = BlockNode::sticky_in(styles);
let fragment = block.layout(vt, styles, self.regions)?;
for (i, frame) in fragment.into_iter().enumerate() {
if i > 0 {
diff --git a/library/src/layout/grid.rs b/library/src/layout/grid.rs
index b6465e1c..5d465f86 100644
--- a/library/src/layout/grid.rs
+++ b/library/src/layout/grid.rs
@@ -262,7 +262,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
}
// Reverse for RTL.
- let is_rtl = styles.get(TextNode::DIR) == Dir::RTL;
+ let is_rtl = TextNode::dir_in(styles) == Dir::RTL;
if is_rtl {
cols.reverse();
}
diff --git a/library/src/layout/hide.rs b/library/src/layout/hide.rs
index 5ba7dea4..43d9a2a8 100644
--- a/library/src/layout/hide.rs
+++ b/library/src/layout/hide.rs
@@ -25,6 +25,6 @@ pub struct HideNode {
impl Show for HideNode {
fn show(&self, _: &mut Vt, _: &Content, _: StyleChain) -> SourceResult<Content> {
- Ok(self.body().styled(MetaNode::DATA, vec![Meta::Hidden]))
+ Ok(self.body().styled(MetaNode::set_data(vec![Meta::Hidden])))
}
}
diff --git a/library/src/layout/list.rs b/library/src/layout/list.rs
index 5ba6b9b0..e6e42263 100644
--- a/library/src/layout/list.rs
+++ b/library/src/layout/list.rs
@@ -127,25 +127,24 @@ impl Layout for ListNode {
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
- let indent = styles.get(Self::INDENT);
- let body_indent = styles.get(Self::BODY_INDENT);
+ let indent = Self::indent_in(styles);
+ let body_indent = Self::body_indent_in(styles);
let gutter = if self.tight() {
- styles.get(ParNode::LEADING).into()
+ ParNode::leading_in(styles).into()
} else {
- styles
- .get(Self::SPACING)
- .unwrap_or_else(|| styles.get(BlockNode::BELOW).amount())
+ Self::spacing_in(styles)
+ .unwrap_or_else(|| BlockNode::below_in(styles).amount())
};
- let depth = styles.get(Self::DEPTH);
- let marker = styles.get(Self::MARKER).resolve(vt.world(), depth)?;
+ let depth = Self::depth_in(styles);
+ let marker = Self::marker_in(styles).resolve(vt.world(), depth)?;
let mut cells = vec![];
for item in self.children() {
cells.push(Content::empty());
cells.push(marker.clone());
cells.push(Content::empty());
- cells.push(item.body().styled(Self::DEPTH, Depth));
+ cells.push(item.body().styled(Self::set_depth(Depth)));
}
let layouter = GridLayouter::new(
diff --git a/library/src/layout/mod.rs b/library/src/layout/mod.rs
index 96d16ec8..6d57912f 100644
--- a/library/src/layout/mod.rs
+++ b/library/src/layout/mod.rs
@@ -450,13 +450,13 @@ impl<'a> FlowBuilder<'a> {
};
if !last_was_parbreak && is_tight_list {
- let leading = styles.get(ParNode::LEADING);
+ let leading = ParNode::leading_in(styles);
let spacing = VNode::list_attach(leading.into());
self.0.push(spacing.pack(), styles);
}
- let above = styles.get(BlockNode::ABOVE);
- let below = styles.get(BlockNode::BELOW);
+ let above = BlockNode::above_in(styles);
+ let below = BlockNode::below_in(styles);
self.0.push(above.clone().pack(), styles);
self.0.push(content.clone(), styles);
self.0.push(below.clone().pack(), styles);
diff --git a/library/src/layout/page.rs b/library/src/layout/page.rs
index 3b557981..0c3de30c 100644
--- a/library/src/layout/page.rs
+++ b/library/src/layout/page.rs
@@ -30,8 +30,8 @@ use crate::prelude::*;
#[node]
#[set({
if let Some(paper) = args.named_or_find::<Paper>("paper")? {
- styles.set(Self::WIDTH, Smart::Custom(paper.width().into()));
- styles.set(Self::HEIGHT, Smart::Custom(paper.height().into()));
+ styles.set(Self::set_width(Smart::Custom(paper.width().into())));
+ styles.set(Self::set_height(Smart::Custom(paper.height().into())));
}
})]
pub struct PageNode {
@@ -260,10 +260,10 @@ impl PageNode {
) -> SourceResult<Fragment> {
// When one of the lengths is infinite the page fits its content along
// that axis.
- let width = styles.get(Self::WIDTH).unwrap_or(Abs::inf());
- let height = styles.get(Self::HEIGHT).unwrap_or(Abs::inf());
+ let width = Self::width_in(styles).unwrap_or(Abs::inf());
+ let height = Self::height_in(styles).unwrap_or(Abs::inf());
let mut size = Size::new(width, height);
- if styles.get(Self::FLIPPED) {
+ if Self::flipped_in(styles) {
std::mem::swap(&mut size.x, &mut size.y);
}
@@ -274,12 +274,12 @@ impl PageNode {
// Determine the margins.
let default = Rel::from(0.1190 * min);
- let padding = styles.get(Self::MARGIN).map(|side| side.unwrap_or(default));
+ let padding = Self::margin_in(styles).map(|side| side.unwrap_or(default));
let mut child = self.body();
// Realize columns.
- let columns = styles.get(Self::COLUMNS);
+ let columns = Self::columns_in(styles);
if columns.get() > 1 {
child = ColumnsNode::new(columns, child).pack();
}
@@ -291,11 +291,11 @@ impl PageNode {
let regions = Regions::repeat(size, size.map(Abs::is_finite));
let mut fragment = child.layout(vt, styles, regions)?;
- let fill = styles.get(Self::FILL);
- let header = styles.get(Self::HEADER);
- let footer = styles.get(Self::FOOTER);
- let foreground = styles.get(Self::FOREGROUND);
- let background = styles.get(Self::BACKGROUND);
+ let fill = Self::fill_in(styles);
+ let header = Self::header_in(styles);
+ let footer = Self::footer_in(styles);
+ let foreground = Self::foreground_in(styles);
+ let background = Self::background_in(styles);
// Realize overlays.
for frame in &mut fragment {
diff --git a/library/src/layout/par.rs b/library/src/layout/par.rs
index 2564940c..10388f88 100644
--- a/library/src/layout/par.rs
+++ b/library/src/layout/par.rs
@@ -2,7 +2,7 @@ use unicode_bidi::{BidiInfo, Level as BidiLevel};
use unicode_script::{Script, UnicodeScript};
use xi_unicode::LineBreakIterator;
-use typst::model::{Key, StyledNode};
+use typst::model::StyledNode;
use super::{BoxNode, HNode, Sizing, Spacing};
use crate::layout::AlignNode;
@@ -495,7 +495,7 @@ fn collect<'a>(
let mut iter = children.iter().peekable();
if consecutive {
- let indent = styles.get(ParNode::INDENT);
+ let indent = ParNode::indent_in(*styles);
if !indent.is_zero()
&& children
.iter()
@@ -530,7 +530,7 @@ fn collect<'a>(
Segment::Text(1)
} else if let Some(node) = child.to::<TextNode>() {
let prev = full.len();
- if let Some(case) = styles.get(TextNode::CASE) {
+ if let Some(case) = TextNode::case_in(styles) {
full.push_str(&case.apply(&node.text()));
} else {
full.push_str(&node.text());
@@ -545,9 +545,9 @@ fn collect<'a>(
Segment::Text(c.len_utf8())
} else if let Some(node) = child.to::<SmartQuoteNode>() {
let prev = full.len();
- if styles.get(SmartQuoteNode::ENABLED) {
- let lang = styles.get(TextNode::LANG);
- let region = styles.get(TextNode::REGION);
+ if SmartQuoteNode::enabled_in(styles) {
+ let lang = TextNode::lang_in(styles);
+ let region = TextNode::region_in(styles);
let quotes = Quotes::from_lang(lang, region);
let peeked = iter.peek().and_then(|child| {
if let Some(node) = child.to::<TextNode>() {
@@ -613,7 +613,7 @@ fn prepare<'a>(
) -> SourceResult<Preparation<'a>> {
let bidi = BidiInfo::new(
text,
- match styles.get(TextNode::DIR) {
+ match TextNode::dir_in(styles) {
Dir::LTR => Some(BidiLevel::ltr()),
Dir::RTL => Some(BidiLevel::rtl()),
_ => None,
@@ -642,7 +642,7 @@ fn prepare<'a>(
Segment::Formula(formula) => {
let pod = Regions::one(region, Axes::splat(false));
let mut frame = formula.layout(vt, styles, pod)?.into_frame();
- frame.translate(Point::with_y(styles.get(TextNode::BASELINE)));
+ frame.translate(Point::with_y(TextNode::baseline_in(styles)));
items.push(Item::Frame(frame));
}
Segment::Box(node) => {
@@ -651,7 +651,7 @@ fn prepare<'a>(
} else {
let pod = Regions::one(region, Axes::splat(false));
let mut frame = node.layout(vt, styles, pod)?.into_frame();
- frame.translate(Point::with_y(styles.get(TextNode::BASELINE)));
+ frame.translate(Point::with_y(TextNode::baseline_in(styles)));
items.push(Item::Frame(frame));
}
}
@@ -664,10 +664,10 @@ fn prepare<'a>(
bidi,
items,
styles,
- hyphenate: shared_get(styles, children, TextNode::HYPHENATE),
- lang: shared_get(styles, children, TextNode::LANG),
- align: styles.get(AlignNode::ALIGNMENT).x.resolve(styles),
- justify: styles.get(ParNode::JUSTIFY),
+ hyphenate: shared_get(styles, children, TextNode::hyphenate_in),
+ lang: shared_get(styles, children, TextNode::lang_in),
+ align: AlignNode::alignment_in(styles).x.resolve(styles),
+ justify: ParNode::justify_in(styles),
})
}
@@ -727,22 +727,23 @@ fn is_compatible(a: Script, b: Script) -> bool {
/// Get a style property, but only if it is the same for all children of the
/// paragraph.
-fn shared_get<'a, K: Key>(
+fn shared_get<'a, T: PartialEq>(
styles: StyleChain<'a>,
children: &[Content],
- key: K,
-) -> Option<K::Output> {
+ getter: fn(StyleChain) -> T,
+) -> Option<T> {
+ let value = getter(styles);
children
.iter()
.filter_map(|child| child.to::<StyledNode>())
- .all(|node| !node.map().contains(key))
- .then(|| styles.get(key))
+ .all(|node| getter(styles.chain(&node.map())) == value)
+ .then(|| value)
}
/// Find suitable linebreaks.
fn linebreak<'a>(vt: &Vt, p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> {
- let linebreaks = p.styles.get(ParNode::LINEBREAKS).unwrap_or_else(|| {
- if p.styles.get(ParNode::JUSTIFY) {
+ let linebreaks = ParNode::linebreaks_in(p.styles).unwrap_or_else(|| {
+ if ParNode::justify_in(p.styles) {
Linebreaks::Optimized
} else {
Linebreaks::Simple
@@ -840,7 +841,7 @@ fn linebreak_optimized<'a>(vt: &Vt, p: &'a Preparation<'a>, width: Abs) -> Vec<L
line: line(vt, p, 0..0, false, false),
}];
- let em = p.styles.get(TextNode::SIZE);
+ let em = TextNode::size_in(p.styles);
for (end, mandatory, hyphen) in breakpoints(p) {
let k = table.len();
@@ -1005,7 +1006,7 @@ impl Breakpoints<'_> {
.hyphenate
.or_else(|| {
let shaped = self.p.find(offset)?.text()?;
- Some(shaped.styles.get(TextNode::HYPHENATE))
+ Some(TextNode::hyphenate_in(shaped.styles))
})
.unwrap_or(false)
}
@@ -1014,7 +1015,7 @@ impl Breakpoints<'_> {
fn lang(&self, offset: usize) -> Option<hypher::Lang> {
let lang = self.p.lang.or_else(|| {
let shaped = self.p.find(offset)?.text()?;
- Some(shaped.styles.get(TextNode::LANG))
+ Some(TextNode::lang_in(shaped.styles))
})?;
let bytes = lang.as_str().as_bytes().try_into().ok()?;
@@ -1155,7 +1156,7 @@ fn finalize(
.collect::<SourceResult<_>>()?;
// Prevent orphans.
- let leading = p.styles.get(ParNode::LEADING);
+ let leading = ParNode::leading_in(p.styles);
if frames.len() >= 2 && !frames[1].is_empty() {
let second = frames.remove(1);
let first = &mut frames[0];
@@ -1199,7 +1200,7 @@ fn commit(
if let Some(Item::Text(text)) = reordered.first() {
if let Some(glyph) = text.glyphs.first() {
if !text.dir.is_positive()
- && text.styles.get(TextNode::OVERHANG)
+ && TextNode::overhang_in(text.styles)
&& (reordered.len() > 1 || text.glyphs.len() > 1)
{
let amount = overhang(glyph.c) * glyph.x_advance.at(text.size);
@@ -1213,7 +1214,7 @@ fn commit(
if let Some(Item::Text(text)) = reordered.last() {
if let Some(glyph) = text.glyphs.last() {
if text.dir.is_positive()
- && text.styles.get(TextNode::OVERHANG)
+ && TextNode::overhang_in(text.styles)
&& (reordered.len() > 1 || text.glyphs.len() > 1)
{
let amount = overhang(glyph.c) * glyph.x_advance.at(text.size);
@@ -1257,7 +1258,7 @@ fn commit(
let region = Size::new(amount, full);
let pod = Regions::one(region, Axes::new(true, false));
let mut frame = node.layout(vt, *styles, pod)?.into_frame();
- frame.translate(Point::with_y(styles.get(TextNode::BASELINE)));
+ frame.translate(Point::with_y(TextNode::baseline_in(*styles)));
push(&mut offset, frame);
} else {
offset += amount;
diff --git a/library/src/layout/repeat.rs b/library/src/layout/repeat.rs
index 67dca285..0fd9ad83 100644
--- a/library/src/layout/repeat.rs
+++ b/library/src/layout/repeat.rs
@@ -40,7 +40,7 @@ impl Layout for RepeatNode {
) -> SourceResult<Fragment> {
let pod = Regions::one(regions.size, Axes::new(false, false));
let piece = self.body().layout(vt, styles, pod)?.into_frame();
- let align = styles.get(AlignNode::ALIGNMENT).x.resolve(styles);
+ let align = AlignNode::alignment_in(styles).x.resolve(styles);
let fill = regions.size.x;
let width = piece.width();
diff --git a/library/src/layout/stack.rs b/library/src/layout/stack.rs
index 430af715..f4e4ab2c 100644
--- a/library/src/layout/stack.rs
+++ b/library/src/layout/stack.rs
@@ -201,10 +201,9 @@ impl<'a> StackLayouter<'a> {
// Block-axis alignment of the `AlignNode` is respected
// by the stack node.
- let aligns = if let Some(styled) = block.to::<StyledNode>() {
- styles.chain(&styled.map()).get(AlignNode::ALIGNMENT)
- } else {
- styles.get(AlignNode::ALIGNMENT)
+ let aligns = match block.to::<StyledNode>() {
+ Some(styled) => AlignNode::alignment_in(styles.chain(&styled.map())),
+ None => AlignNode::alignment_in(styles),
};
let aligns = aligns.resolve(styles);
diff --git a/library/src/layout/table.rs b/library/src/layout/table.rs
index 0083a7bf..1bd47df0 100644
--- a/library/src/layout/table.rs
+++ b/library/src/layout/table.rs
@@ -128,8 +128,8 @@ impl Layout for TableNode {
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
- let inset = styles.get(Self::INSET);
- let align = styles.get(Self::ALIGN);
+ let inset = Self::inset_in(styles);
+ let align = Self::align_in(styles);
let tracks = Axes::new(self.columns().0, self.rows().0);
let gutter = Axes::new(self.column_gutter().0, self.row_gutter().0);
@@ -144,15 +144,15 @@ impl Layout for TableNode {
let x = i % cols;
let y = i / cols;
if let Smart::Custom(alignment) = align.resolve(vt, x, y)? {
- child = child.styled(AlignNode::ALIGNMENT, alignment)
+ child = child.styled(AlignNode::set_alignment(alignment));
}
Ok(child)
})
.collect::<SourceResult<_>>()?;
- let fill = styles.get(Self::FILL);
- let stroke = styles.get(Self::STROKE).map(PartialStroke::unwrap_or_default);
+ let fill = Self::fill_in(styles);
+ let stroke = Self::stroke_in(styles).map(PartialStroke::unwrap_or_default);
// Prepare grid layout by unifying content and gutter tracks.
let layouter = GridLayouter::new(
diff --git a/library/src/layout/terms.rs b/library/src/layout/terms.rs
index 2933ea20..d859a447 100644
--- a/library/src/layout/terms.rs
+++ b/library/src/layout/terms.rs
@@ -90,14 +90,13 @@ impl Layout for TermsNode {
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
- let indent = styles.get(Self::INDENT);
- let body_indent = styles.get(Self::HANGING_INDENT);
+ let indent = Self::indent_in(styles);
+ let body_indent = Self::hanging_indent_in(styles);
let gutter = if self.tight() {
- styles.get(ParNode::LEADING).into()
+ ParNode::leading_in(styles).into()
} else {
- styles
- .get(Self::SPACING)
- .unwrap_or_else(|| styles.get(BlockNode::BELOW).amount())
+ Self::spacing_in(styles)
+ .unwrap_or_else(|| BlockNode::below_in(styles).amount())
};
let mut cells = vec![];
diff --git a/library/src/layout/transform.rs b/library/src/layout/transform.rs
index b71cfa9f..41d3d120 100644
--- a/library/src/layout/transform.rs
+++ b/library/src/layout/transform.rs
@@ -122,7 +122,7 @@ impl Layout for RotateNode {
) -> SourceResult<Fragment> {
let pod = Regions::one(regions.base(), Axes::splat(false));
let mut frame = self.body().layout(vt, styles, pod)?.into_frame();
- let origin = styles.get(Self::ORIGIN).unwrap_or(Align::CENTER_HORIZON);
+ let origin = Self::origin_in(styles).unwrap_or(Align::CENTER_HORIZON);
let Axes { x, y } = origin.zip(frame.size()).map(|(o, s)| o.position(s));
let ts = Transform::translate(x, y)
.pre_concat(Transform::rotate(self.angle()))
@@ -199,7 +199,7 @@ impl Layout for ScaleNode {
) -> SourceResult<Fragment> {
let pod = Regions::one(regions.base(), Axes::splat(false));
let mut frame = self.body().layout(vt, styles, pod)?.into_frame();
- let origin = styles.get(Self::ORIGIN).unwrap_or(Align::CENTER_HORIZON);
+ let origin = Self::origin_in(styles).unwrap_or(Align::CENTER_HORIZON);
let Axes { x, y } = origin.zip(frame.size()).map(|(o, s)| o.position(s));
let transform = Transform::translate(x, y)
.pre_concat(Transform::scale(self.x(), self.y()))
diff --git a/library/src/lib.rs b/library/src/lib.rs
index e0994f25..fcd0bf45 100644
--- a/library/src/lib.rs
+++ b/library/src/lib.rs
@@ -164,8 +164,8 @@ fn styles() -> StyleMap {
fn items() -> LangItems {
LangItems {
layout: |world, content, styles| content.layout_root(world, styles),
- em: |styles| styles.get(text::TextNode::SIZE),
- dir: |styles| styles.get(text::TextNode::DIR),
+ em: text::TextNode::size_in,
+ dir: text::TextNode::dir_in,
space: || text::SpaceNode::new().pack(),
linebreak: || text::LinebreakNode::new().pack(),
text: |text| text::TextNode::new(text).pack(),
@@ -178,7 +178,7 @@ fn items() -> LangItems {
raw: |text, lang, block| {
let content = text::RawNode::new(text).with_block(block).pack();
match lang {
- Some(_) => content.styled(text::RawNode::LANG, lang),
+ Some(_) => content.styled(text::RawNode::set_lang(lang)),
None => content,
}
},
diff --git a/library/src/math/ctx.rs b/library/src/math/ctx.rs
index a3aa4047..991d582c 100644
--- a/library/src/math/ctx.rs
+++ b/library/src/math/ctx.rs
@@ -49,7 +49,7 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
) -> Self {
let table = font.ttf().tables().math.unwrap();
let constants = table.constants.unwrap();
- let size = styles.get(TextNode::SIZE);
+ let size = TextNode::size_in(styles);
let ttf = font.ttf();
let space_width = ttf
.glyph_index(' ')
@@ -175,21 +175,20 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
pub fn style(&mut self, style: MathStyle) {
self.style_stack.push((self.style, self.size));
- let base_size = self.styles().get(TextNode::SIZE) / self.style.size.factor(self);
+ let base_size = TextNode::size_in(self.styles()) / self.style.size.factor(self);
self.size = base_size * style.size.factor(self);
- self.map.set(TextNode::SIZE, TextSize(self.size.into()));
- self.map.set(
- TextNode::STYLE,
- if style.italic == Smart::Custom(true) {
+ self.map.set(TextNode::set_size(TextSize(self.size.into())));
+ self.map
+ .set(TextNode::set_style(if style.italic == Smart::Custom(true) {
FontStyle::Italic
} else {
FontStyle::Normal
- },
- );
- self.map.set(
- TextNode::WEIGHT,
- if style.bold { FontWeight::BOLD } else { FontWeight::REGULAR },
- );
+ }));
+ self.map.set(TextNode::set_weight(if style.bold {
+ FontWeight::BOLD
+ } else {
+ FontWeight::REGULAR
+ }));
self.style = style;
}
diff --git a/library/src/math/frac.rs b/library/src/math/frac.rs
index ea647fc5..c1f4065b 100644
--- a/library/src/math/frac.rs
+++ b/library/src/math/frac.rs
@@ -133,7 +133,7 @@ fn layout(
line_pos,
Element::Shape(
Geometry::Line(Point::with_x(line_width)).stroked(Stroke {
- paint: ctx.styles().get(TextNode::FILL),
+ paint: TextNode::fill_in(ctx.styles()),
thickness,
}),
),
diff --git a/library/src/math/fragment.rs b/library/src/math/fragment.rs
index 93c5946b..73daa4b2 100644
--- a/library/src/math/fragment.rs
+++ b/library/src/math/fragment.rs
@@ -180,8 +180,8 @@ impl GlyphFragment {
id,
c,
font: ctx.font.clone(),
- lang: ctx.styles().get(TextNode::LANG),
- fill: ctx.styles().get(TextNode::FILL),
+ lang: TextNode::lang_in(ctx.styles()),
+ fill: TextNode::fill_in(ctx.styles()),
style: ctx.style,
font_size: ctx.size,
width,
diff --git a/library/src/math/matrix.rs b/library/src/math/matrix.rs
index c7303735..d4bf52f3 100644
--- a/library/src/math/matrix.rs
+++ b/library/src/math/matrix.rs
@@ -35,7 +35,7 @@ pub struct VecNode {
impl LayoutMath for VecNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- let delim = ctx.styles().get(Self::DELIM);
+ let delim = Self::delim_in(ctx.styles());
let frame = layout_vec_body(ctx, &self.children(), Align::Center)?;
layout_delimiters(ctx, frame, Some(delim.open()), Some(delim.close()))
}
@@ -115,7 +115,7 @@ impl Construct for MatNode {
impl LayoutMath for MatNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- let delim = ctx.styles().get(Self::DELIM);
+ let delim = Self::delim_in(ctx.styles());
let frame = layout_mat_body(ctx, &self.rows())?;
layout_delimiters(ctx, frame, Some(delim.open()), Some(delim.close()))
}
@@ -156,7 +156,7 @@ pub struct CasesNode {
impl LayoutMath for CasesNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- let delim = ctx.styles().get(Self::DELIM);
+ let delim = Self::delim_in(ctx.styles());
let frame = layout_vec_body(ctx, &self.children(), Align::Left)?;
layout_delimiters(ctx, frame, Some(delim.open()), None)
}
diff --git a/library/src/math/mod.rs b/library/src/math/mod.rs
index 6f5e5907..3f0b0607 100644
--- a/library/src/math/mod.rs
+++ b/library/src/math/mod.rs
@@ -158,11 +158,10 @@ impl Show for FormulaNode {
impl Finalize for FormulaNode {
fn finalize(&self, realized: Content) -> Content {
realized
- .styled(TextNode::WEIGHT, FontWeight::from_number(450))
- .styled(
- TextNode::FONT,
- FontList(vec![FontFamily::new("New Computer Modern Math")]),
- )
+ .styled(TextNode::set_weight(FontWeight::from_number(450)))
+ .styled(TextNode::set_font(FontList(vec![FontFamily::new(
+ "New Computer Modern Math",
+ )])))
}
}
@@ -196,10 +195,10 @@ impl Layout for FormulaNode {
let mut frame = ctx.layout_frame(self)?;
if !block {
- let slack = styles.get(ParNode::LEADING) * 0.7;
- let top_edge = styles.get(TextNode::TOP_EDGE).resolve(styles, font.metrics());
+ let slack = ParNode::leading_in(styles) * 0.7;
+ let top_edge = TextNode::top_edge_in(styles).resolve(styles, font.metrics());
let bottom_edge =
- -styles.get(TextNode::BOTTOM_EDGE).resolve(styles, font.metrics());
+ -TextNode::bottom_edge_in(styles).resolve(styles, font.metrics());
let ascent = top_edge.max(frame.ascent() - slack);
let descent = bottom_edge.max(frame.descent() - slack);
@@ -232,7 +231,9 @@ impl LayoutMath for Content {
if let Some(styled) = self.to::<StyledNode>() {
let map = styled.map();
- if map.contains(TextNode::FONT) {
+ if TextNode::font_in(ctx.styles().chain(&map))
+ != TextNode::font_in(ctx.styles())
+ {
let frame = ctx.layout_content(self)?;
ctx.push(FrameFragment::new(ctx, frame).with_spaced(true));
return Ok(());
@@ -241,7 +242,7 @@ impl LayoutMath for Content {
let prev_map = std::mem::replace(&mut ctx.map, map);
let prev_size = ctx.size;
ctx.map.apply(prev_map.clone());
- ctx.size = ctx.styles().get(TextNode::SIZE);
+ ctx.size = TextNode::size_in(ctx.styles());
styled.body().layout_math(ctx)?;
ctx.size = prev_size;
ctx.map = prev_map;
diff --git a/library/src/math/root.rs b/library/src/math/root.rs
index 191acb94..e190c65f 100644
--- a/library/src/math/root.rs
+++ b/library/src/math/root.rs
@@ -128,7 +128,7 @@ fn layout(
line_pos,
Element::Shape(
Geometry::Line(Point::with_x(radicand.width()))
- .stroked(Stroke { paint: ctx.styles().get(TextNode::FILL), thickness }),
+ .stroked(Stroke { paint: TextNode::fill_in(ctx.styles()), thickness }),
),
);
diff --git a/library/src/math/row.rs b/library/src/math/row.rs
index b7720c14..ecb2e31e 100644
--- a/library/src/math/row.rs
+++ b/library/src/math/row.rs
@@ -103,7 +103,7 @@ impl MathRow {
pub fn to_frame(self, ctx: &MathContext) -> Frame {
let styles = ctx.styles();
- let align = styles.get(AlignNode::ALIGNMENT).x.resolve(styles);
+ let align = AlignNode::alignment_in(styles).x.resolve(styles);
self.to_aligned_frame(ctx, &[], align)
}
@@ -124,7 +124,7 @@ impl MathRow {
if self.iter().any(|frag| matches!(frag, MathFragment::Linebreak)) {
let fragments: Vec<_> = std::mem::take(&mut self.0);
let leading = if ctx.style.size >= MathSize::Text {
- ctx.styles().get(ParNode::LEADING)
+ ParNode::leading_in(ctx.styles())
} else {
TIGHT_LEADING.scaled(ctx)
};
diff --git a/library/src/meta/document.rs b/library/src/meta/document.rs
index 29ed38fe..1325d85b 100644
--- a/library/src/meta/document.rs
+++ b/library/src/meta/document.rs
@@ -58,8 +58,8 @@ impl LayoutRoot for DocumentNode {
Ok(Document {
pages,
- title: styles.get(Self::TITLE).clone(),
- author: styles.get(Self::AUTHOR).0.clone(),
+ title: Self::title_in(styles),
+ author: Self::author_in(styles).0,
})
}
}
diff --git a/library/src/meta/heading.rs b/library/src/meta/heading.rs
index bd95001b..f0107a6a 100644
--- a/library/src/meta/heading.rs
+++ b/library/src/meta/heading.rs
@@ -106,15 +106,15 @@ impl Prepare for HeadingNode {
}
let mut numbers = Value::None;
- if let Some(numbering) = styles.get(Self::NUMBERING) {
+ if let Some(numbering) = Self::numbering_in(styles) {
numbers = numbering.apply(vt.world(), counter.advance(self))?;
}
- this.push_field("outlined", Value::Bool(styles.get(Self::OUTLINED)));
+ this.push_field("outlined", Value::Bool(Self::outlined_in(styles)));
this.push_field("numbers", numbers);
let meta = Meta::Node(my_id, this.clone());
- Ok(this.styled(MetaNode::DATA, vec![meta]))
+ Ok(this.styled(MetaNode::set_data(vec![meta])))
}
}
@@ -145,11 +145,11 @@ impl Finalize for HeadingNode {
let below = Em::new(0.75) / scale;
let mut map = StyleMap::new();
- map.set(TextNode::SIZE, TextSize(size.into()));
- map.set(TextNode::WEIGHT, FontWeight::BOLD);
- map.set(BlockNode::ABOVE, VNode::block_around(above.into()));
- map.set(BlockNode::BELOW, VNode::block_around(below.into()));
- map.set(BlockNode::STICKY, true);
+ map.set(TextNode::set_size(TextSize(size.into())));
+ map.set(TextNode::set_weight(FontWeight::BOLD));
+ map.set(BlockNode::set_above(VNode::block_around(above.into())));
+ map.set(BlockNode::set_below(VNode::block_around(below.into())));
+ map.set(BlockNode::set_sticky(true));
realized.styled_with_map(map)
}
}
diff --git a/library/src/meta/link.rs b/library/src/meta/link.rs
index 4f07aaee..32b70153 100644
--- a/library/src/meta/link.rs
+++ b/library/src/meta/link.rs
@@ -91,8 +91,8 @@ impl Show for LinkNode {
impl Finalize for LinkNode {
fn finalize(&self, realized: Content) -> Content {
realized
- .styled(MetaNode::DATA, vec![Meta::Link(self.dest())])
- .styled(TextNode::HYPHENATE, Hyphenate(Smart::Custom(false)))
+ .styled(MetaNode::set_data(vec![Meta::Link(self.dest())]))
+ .styled(TextNode::set_hyphenate(Hyphenate(Smart::Custom(false))))
}
}
diff --git a/library/src/meta/outline.rs b/library/src/meta/outline.rs
index 4b5a44f7..3ccc991e 100644
--- a/library/src/meta/outline.rs
+++ b/library/src/meta/outline.rs
@@ -102,9 +102,9 @@ impl Show for OutlineNode {
styles: StyleChain,
) -> SourceResult<Content> {
let mut seq = vec![ParbreakNode::new().pack()];
- if let Some(title) = styles.get(Self::TITLE) {
+ if let Some(title) = Self::title_in(styles) {
let title = title.clone().unwrap_or_else(|| {
- TextNode::packed(match styles.get(TextNode::LANG) {
+ TextNode::packed(match TextNode::lang_in(styles) {
Lang::GERMAN => "Inhaltsverzeichnis",
Lang::ENGLISH | _ => "Contents",
})
@@ -113,13 +113,13 @@ impl Show for OutlineNode {
seq.push(
HeadingNode::new(title)
.pack()
- .styled(HeadingNode::NUMBERING, None)
- .styled(HeadingNode::OUTLINED, false),
+ .styled(HeadingNode::set_numbering(None))
+ .styled(HeadingNode::set_outlined(false)),
);
}
- let indent = styles.get(Self::INDENT);
- let depth = styles.get(Self::DEPTH);
+ let indent = Self::indent_in(styles);
+ let depth = Self::depth_in(styles);
let mut ancestors: Vec<&Content> = vec![];
for (_, node) in vt.locate(Selector::node::<HeadingNode>()) {
@@ -171,7 +171,7 @@ impl Show for OutlineNode {
seq.push(start.linked(Destination::Internal(loc)));
// Add filler symbols between the section name and page number.
- if let Some(filler) = styles.get(Self::FILL) {
+ if let Some(filler) = Self::fill_in(styles) {
seq.push(SpaceNode::new().pack());
seq.push(
BoxNode::new()
diff --git a/library/src/shared/ext.rs b/library/src/shared/ext.rs
index f797e135..44f933e2 100644
--- a/library/src/shared/ext.rs
+++ b/library/src/shared/ext.rs
@@ -1,6 +1,8 @@
//! Extension traits.
+use crate::layout::{AlignNode, MoveNode, PadNode};
use crate::prelude::*;
+use crate::text::{EmphNode, FontFamily, FontList, StrongNode, TextNode, UnderlineNode};
/// Additional methods on content.
pub trait ContentExt {
@@ -28,27 +30,27 @@ pub trait ContentExt {
impl ContentExt for Content {
fn strong(self) -> Self {
- crate::text::StrongNode::new(self).pack()
+ StrongNode::new(self).pack()
}
fn emph(self) -> Self {
- crate::text::EmphNode::new(self).pack()
+ EmphNode::new(self).pack()
}
fn underlined(self) -> Self {
- crate::text::UnderlineNode::new(self).pack()
+ UnderlineNode::new(self).pack()
}
fn linked(self, dest: Destination) -> Self {
- self.styled(MetaNode::DATA, vec![Meta::Link(dest.clone())])
+ self.styled(MetaNode::set_data(vec![Meta::Link(dest.clone())]))
}
fn aligned(self, aligns: Axes<Option<GenAlign>>) -> Self {
- self.styled(crate::layout::AlignNode::ALIGNMENT, aligns)
+ self.styled(AlignNode::set_alignment(aligns))
}
fn padded(self, padding: Sides<Rel<Length>>) -> Self {
- crate::layout::PadNode::new(self)
+ PadNode::new(self)
.with_left(padding.left)
.with_top(padding.top)
.with_right(padding.right)
@@ -57,10 +59,7 @@ impl ContentExt for Content {
}
fn moved(self, delta: Axes<Rel<Length>>) -> Self {
- crate::layout::MoveNode::new(self)
- .with_dx(delta.x)
- .with_dy(delta.y)
- .pack()
+ MoveNode::new(self).with_dx(delta.x).with_dy(delta.y).pack()
}
}
@@ -68,18 +67,15 @@ impl ContentExt for Content {
pub trait StyleMapExt {
/// Set a font family composed of a preferred family and existing families
/// from a style chain.
- fn set_family(&mut self, preferred: crate::text::FontFamily, existing: StyleChain);
+ fn set_family(&mut self, preferred: FontFamily, existing: StyleChain);
}
impl StyleMapExt for StyleMap {
- fn set_family(&mut self, preferred: crate::text::FontFamily, existing: StyleChain) {
- self.set(
- crate::text::TextNode::FONT,
- crate::text::FontList(
- std::iter::once(preferred)
- .chain(existing.get(crate::text::TextNode::FONT).0.iter().cloned())
- .collect(),
- ),
- );
+ fn set_family(&mut self, preferred: FontFamily, existing: StyleChain) {
+ self.set(TextNode::set_font(FontList(
+ std::iter::once(preferred)
+ .chain(TextNode::font_in(existing))
+ .collect(),
+ )));
}
}
diff --git a/library/src/text/deco.rs b/library/src/text/deco.rs
index d47b336b..1b3167e1 100644
--- a/library/src/text/deco.rs
+++ b/library/src/text/deco.rs
@@ -75,16 +75,13 @@ pub struct UnderlineNode {
impl Show for UnderlineNode {
fn show(&self, _: &mut Vt, _: &Content, styles: StyleChain) -> SourceResult<Content> {
- Ok(self.body().styled(
- TextNode::DECO,
- Decoration {
- line: DecoLine::Underline,
- stroke: styles.get(Self::STROKE).unwrap_or_default(),
- offset: styles.get(Self::OFFSET),
- extent: styles.get(Self::EXTENT),
- evade: styles.get(Self::EVADE),
- },
- ))
+ Ok(self.body().styled(TextNode::set_deco(Decoration {
+ line: DecoLine::Underline,
+ stroke: Self::stroke_in(styles).unwrap_or_default(),
+ offset: Self::offset_in(styles),
+ extent: Self::extent_in(styles),
+ evade: Self::evade_in(styles),
+ })))
}
}
@@ -165,16 +162,13 @@ pub struct OverlineNode {
impl Show for OverlineNode {
fn show(&self, _: &mut Vt, _: &Content, styles: StyleChain) -> SourceResult<Content> {
- Ok(self.body().styled(
- TextNode::DECO,
- Decoration {
- line: DecoLine::Overline,
- stroke: styles.get(Self::STROKE).unwrap_or_default(),
- offset: styles.get(Self::OFFSET),
- extent: styles.get(Self::EXTENT),
- evade: styles.get(Self::EVADE),
- },
- ))
+ Ok(self.body().styled(TextNode::set_deco(Decoration {
+ line: DecoLine::Overline,
+ stroke: Self::stroke_in(styles).unwrap_or_default(),
+ offset: Self::offset_in(styles),
+ extent: Self::extent_in(styles),
+ evade: Self::evade_in(styles),
+ })))
}
}
@@ -239,16 +233,13 @@ pub struct StrikeNode {
impl Show for StrikeNode {
fn show(&self, _: &mut Vt, _: &Content, styles: StyleChain) -> SourceResult<Content> {
- Ok(self.body().styled(
- TextNode::DECO,
- Decoration {
- line: DecoLine::Strikethrough,
- stroke: styles.get(Self::STROKE).unwrap_or_default(),
- offset: styles.get(Self::OFFSET),
- extent: styles.get(Self::EXTENT),
- evade: false,
- },
- ))
+ Ok(self.body().styled(TextNode::set_deco(Decoration {
+ line: DecoLine::Strikethrough,
+ stroke: Self::stroke_in(styles).unwrap_or_default(),
+ offset: Self::offset_in(styles),
+ extent: Self::extent_in(styles),
+ evade: false,
+ })))
}
}
diff --git a/library/src/text/misc.rs b/library/src/text/misc.rs
index 9caaf68e..91ac1748 100644
--- a/library/src/text/misc.rs
+++ b/library/src/text/misc.rs
@@ -103,7 +103,7 @@ pub struct StrongNode {
impl Show for StrongNode {
fn show(&self, _: &mut Vt, _: &Content, styles: StyleChain) -> SourceResult<Content> {
- Ok(self.body().styled(TextNode::DELTA, Delta(styles.get(Self::DELTA))))
+ Ok(self.body().styled(TextNode::set_delta(Delta(Self::delta_in(styles)))))
}
}
@@ -164,7 +164,7 @@ pub struct EmphNode {
impl Show for EmphNode {
fn show(&self, _: &mut Vt, _: &Content, _: StyleChain) -> SourceResult<Content> {
- Ok(self.body().styled(TextNode::EMPH, Toggle))
+ Ok(self.body().styled(TextNode::set_emph(Toggle)))
}
}
@@ -233,7 +233,7 @@ pub fn upper(args: &mut Args) -> SourceResult<Value> {
fn case(case: Case, args: &mut Args) -> SourceResult<Value> {
Ok(match args.expect("string or content")? {
ToCase::Str(v) => Value::Str(case.apply(&v).into()),
- ToCase::Content(v) => Value::Content(v.styled(TextNode::CASE, Some(case))),
+ ToCase::Content(v) => Value::Content(v.styled(TextNode::set_case(Some(case)))),
})
}
@@ -313,7 +313,7 @@ cast_to_value! {
#[func]
pub fn smallcaps(args: &mut Args) -> SourceResult<Value> {
let body: Content = args.expect("content")?;
- Ok(Value::Content(body.styled(TextNode::SMALLCAPS, true)))
+ Ok(Value::Content(body.styled(TextNode::set_smallcaps(true))))
}
/// Create blind text.
diff --git a/library/src/text/mod.rs b/library/src/text/mod.rs
index 329ff6a9..2318c426 100644
--- a/library/src/text/mod.rs
+++ b/library/src/text/mod.rs
@@ -578,6 +578,15 @@ cast_to_value! {
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
pub struct FontList(pub Vec<FontFamily>);
+impl IntoIterator for FontList {
+ type IntoIter = std::vec::IntoIter<FontFamily>;
+ type Item = FontFamily;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.0.into_iter()
+ }
+}
+
cast_from_value! {
FontList,
family: FontFamily => Self(vec![family]),
@@ -664,7 +673,7 @@ impl Resolve for HorizontalDir {
fn resolve(self, styles: StyleChain) -> Self::Output {
match self.0 {
- Smart::Auto => styles.get(TextNode::LANG).dir(),
+ Smart::Auto => TextNode::lang_in(styles).dir(),
Smart::Custom(dir) => dir,
}
}
@@ -688,7 +697,7 @@ impl Resolve for Hyphenate {
fn resolve(self, styles: StyleChain) -> Self::Output {
match self.0 {
- Smart::Auto => styles.get(ParNode::JUSTIFY),
+ Smart::Auto => ParNode::justify_in(styles),
Smart::Custom(v) => v,
}
}
diff --git a/library/src/text/raw.rs b/library/src/text/raw.rs
index 4eefb0ea..8526a278 100644
--- a/library/src/text/raw.rs
+++ b/library/src/text/raw.rs
@@ -114,7 +114,7 @@ impl Prepare for RawNode {
mut this: Content,
styles: StyleChain,
) -> SourceResult<Content> {
- this.push_field("lang", styles.get(Self::LANG).clone());
+ this.push_field("lang", Self::lang_in(styles).clone());
Ok(this)
}
}
@@ -122,7 +122,7 @@ impl Prepare for RawNode {
impl Show for RawNode {
fn show(&self, _: &mut Vt, _: &Content, styles: StyleChain) -> SourceResult<Content> {
let text = self.text();
- let lang = styles.get(Self::LANG).as_ref().map(|s| s.to_lowercase());
+ let lang = Self::lang_in(styles).as_ref().map(|s| s.to_lowercase());
let foreground = THEME
.settings
.foreground
@@ -181,11 +181,11 @@ impl Show for RawNode {
impl Finalize for RawNode {
fn finalize(&self, realized: Content) -> Content {
let mut map = StyleMap::new();
- map.set(TextNode::OVERHANG, false);
- map.set(TextNode::HYPHENATE, Hyphenate(Smart::Custom(false)));
- map.set(TextNode::SIZE, TextSize(Em::new(0.8).into()));
- map.set(TextNode::FONT, FontList(vec![FontFamily::new("DejaVu Sans Mono")]));
- map.set(SmartQuoteNode::ENABLED, false);
+ map.set(TextNode::set_overhang(false));
+ map.set(TextNode::set_hyphenate(Hyphenate(Smart::Custom(false))));
+ map.set(TextNode::set_size(TextSize(Em::new(0.8).into())));
+ map.set(TextNode::set_font(FontList(vec![FontFamily::new("DejaVu Sans Mono")])));
+ map.set(SmartQuoteNode::set_enabled(false));
realized.styled_with_map(map)
}
}
@@ -221,7 +221,7 @@ fn styled(piece: &str, foreground: Paint, style: synt::Style) -> Content {
let paint = to_typst(style.foreground).into();
if paint != foreground {
- body = body.styled(TextNode::FILL, paint);
+ body = body.styled(TextNode::set_fill(paint));
}
if style.font_style.contains(synt::FontStyle::BOLD) {
diff --git a/library/src/text/shaping.rs b/library/src/text/shaping.rs
index e1b12120..e7ce4027 100644
--- a/library/src/text/shaping.rs
+++ b/library/src/text/shaping.rs
@@ -88,10 +88,10 @@ impl<'a> ShapedText<'a> {
let mut frame = Frame::new(size);
frame.set_baseline(top);
- let shift = self.styles.get(TextNode::BASELINE);
- let lang = self.styles.get(TextNode::LANG);
- let decos = self.styles.get(TextNode::DECO);
- let fill = self.styles.get(TextNode::FILL);
+ let shift = TextNode::baseline_in(self.styles);
+ let lang = TextNode::lang_in(self.styles);
+ let decos = TextNode::deco_in(self.styles);
+ let fill = TextNode::fill_in(self.styles);
for ((font, y_offset), group) in
self.glyphs.as_ref().group_by_key(|g| (g.font.clone(), g.y_offset))
@@ -137,8 +137,8 @@ impl<'a> ShapedText<'a> {
let mut top = Abs::zero();
let mut bottom = Abs::zero();
- let top_edge = self.styles.get(TextNode::TOP_EDGE);
- let bottom_edge = self.styles.get(TextNode::BOTTOM_EDGE);
+ let top_edge = TextNode::top_edge_in(self.styles);
+ let bottom_edge = TextNode::bottom_edge_in(self.styles);
// Expand top and bottom by reading the font's vertical metrics.
let mut expand = |font: &Font| {
@@ -315,8 +315,7 @@ pub fn shape<'a>(
styles: StyleChain<'a>,
dir: Dir,
) -> ShapedText<'a> {
- let size = styles.get(TextNode::SIZE);
-
+ let size = TextNode::size_in(styles);
let mut ctx = ShapingContext {
vt,
size,
@@ -325,7 +324,7 @@ pub fn shape<'a>(
styles,
variant: variant(styles),
tags: tags(styles),
- fallback: styles.get(TextNode::FALLBACK),
+ fallback: TextNode::fallback_in(styles),
dir,
};
@@ -494,11 +493,9 @@ 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_length(ctx.styles.get(TextNode::TRACKING), ctx.size);
- let spacing = ctx
- .styles
- .get(TextNode::SPACING)
- .map(|abs| Em::from_length(abs, ctx.size));
+ let tracking = Em::from_length(TextNode::tracking_in(ctx.styles), ctx.size);
+ let spacing =
+ TextNode::spacing_in(ctx.styles).map(|abs| Em::from_length(abs, ctx.size));
let mut glyphs = ctx.glyphs.iter_mut().peekable();
while let Some(glyph) = glyphs.next() {
@@ -527,17 +524,17 @@ fn nbsp_delta(font: &Font) -> Option<Em> {
/// Resolve the font variant.
pub fn variant(styles: StyleChain) -> FontVariant {
let mut variant = FontVariant::new(
- styles.get(TextNode::STYLE),
- styles.get(TextNode::WEIGHT),
- styles.get(TextNode::STRETCH),
+ TextNode::style_in(styles),
+ TextNode::weight_in(styles),
+ TextNode::stretch_in(styles),
);
- let delta = styles.get(TextNode::DELTA);
+ let delta = TextNode::delta_in(styles);
variant.weight = variant
.weight
.thicken(delta.clamp(i16::MIN as i64, i16::MAX as i64) as i16);
- if styles.get(TextNode::EMPH) {
+ if TextNode::emph_in(styles) {
variant.style = match variant.style {
FontStyle::Normal => FontStyle::Italic,
FontStyle::Italic => FontStyle::Normal,
@@ -558,10 +555,8 @@ pub fn families(styles: StyleChain) -> impl Iterator<Item = FontFamily> + Clone
"segoe ui emoji",
];
- let tail = if styles.get(TextNode::FALLBACK) { FALLBACKS } else { &[] };
- styles
- .get(TextNode::FONT)
- .0
+ let tail = if TextNode::fallback_in(styles) { FALLBACKS } else { &[] };
+ TextNode::font_in(styles)
.into_iter()
.chain(tail.iter().copied().map(FontFamily::new))
}
@@ -574,59 +569,59 @@ fn tags(styles: StyleChain) -> Vec<Feature> {
};
// Features that are on by default in Harfbuzz are only added if disabled.
- if !styles.get(TextNode::KERNING) {
+ if !TextNode::kerning_in(styles) {
feat(b"kern", 0);
}
// Features that are off by default in Harfbuzz are only added if enabled.
- if styles.get(TextNode::SMALLCAPS) {
+ if TextNode::smallcaps_in(styles) {
feat(b"smcp", 1);
}
- if styles.get(TextNode::ALTERNATES) {
+ if TextNode::alternates_in(styles) {
feat(b"salt", 1);
}
let storage;
- if let Some(set) = styles.get(TextNode::STYLISTIC_SET) {
+ if let Some(set) = TextNode::stylistic_set_in(styles) {
storage = [b's', b's', b'0' + set.get() / 10, b'0' + set.get() % 10];
feat(&storage, 1);
}
- if !styles.get(TextNode::LIGATURES) {
+ if !TextNode::ligatures_in(styles) {
feat(b"liga", 0);
feat(b"clig", 0);
}
- if styles.get(TextNode::DISCRETIONARY_LIGATURES) {
+ if TextNode::discretionary_ligatures_in(styles) {
feat(b"dlig", 1);
}
- if styles.get(TextNode::HISTORICAL_LIGATURES) {
+ if TextNode::historical_ligatures_in(styles) {
feat(b"hilg", 1);
}
- match styles.get(TextNode::NUMBER_TYPE) {
+ match TextNode::number_type_in(styles) {
Smart::Auto => {}
Smart::Custom(NumberType::Lining) => feat(b"lnum", 1),
Smart::Custom(NumberType::OldStyle) => feat(b"onum", 1),
}
- match styles.get(TextNode::NUMBER_WIDTH) {
+ match TextNode::number_width_in(styles) {
Smart::Auto => {}
Smart::Custom(NumberWidth::Proportional) => feat(b"pnum", 1),
Smart::Custom(NumberWidth::Tabular) => feat(b"tnum", 1),
}
- if styles.get(TextNode::SLASHED_ZERO) {
+ if TextNode::slashed_zero_in(styles) {
feat(b"zero", 1);
}
- if styles.get(TextNode::FRACTIONS) {
+ if TextNode::fractions_in(styles) {
feat(b"frac", 1);
}
- for (tag, value) in styles.get(TextNode::FEATURES).0 {
+ for (tag, value) in TextNode::features_in(styles).0 {
tags.push(Feature::new(tag, value, ..))
}
@@ -636,8 +631,8 @@ fn tags(styles: StyleChain) -> Vec<Feature> {
/// Process the language and and region of a style chain into a
/// rustybuzz-compatible BCP 47 language.
fn language(styles: StyleChain) -> rustybuzz::Language {
- let mut bcp: EcoString = styles.get(TextNode::LANG).as_str().into();
- if let Some(region) = styles.get(TextNode::REGION) {
+ let mut bcp: EcoString = TextNode::lang_in(styles).as_str().into();
+ if let Some(region) = TextNode::region_in(styles) {
bcp.push('-');
bcp.push_str(region.as_str());
}
diff --git a/library/src/text/shift.rs b/library/src/text/shift.rs
index c44cc3b0..98718365 100644
--- a/library/src/text/shift.rs
+++ b/library/src/text/shift.rs
@@ -59,7 +59,7 @@ impl Show for SubNode {
) -> SourceResult<Content> {
let body = self.body();
let mut transformed = None;
- if styles.get(Self::TYPOGRAPHIC) {
+ if Self::typographic_in(styles) {
if let Some(text) = search_text(&body, true) {
if is_shapable(vt, &text, styles) {
transformed = Some(TextNode::packed(text));
@@ -68,10 +68,8 @@ impl Show for SubNode {
};
Ok(transformed.unwrap_or_else(|| {
- let mut map = StyleMap::new();
- map.set(TextNode::BASELINE, styles.get(Self::BASELINE));
- map.set(TextNode::SIZE, styles.get(Self::SIZE));
- body.styled_with_map(map)
+ body.styled(TextNode::set_baseline(Self::baseline_in(styles)))
+ .styled(TextNode::set_size(Self::size_in(styles)))
}))
}
}
@@ -132,7 +130,7 @@ impl Show for SuperNode {
) -> SourceResult<Content> {
let body = self.body();
let mut transformed = None;
- if styles.get(Self::TYPOGRAPHIC) {
+ if Self::typographic_in(styles) {
if let Some(text) = search_text(&body, false) {
if is_shapable(vt, &text, styles) {
transformed = Some(TextNode::packed(text));
@@ -141,10 +139,8 @@ impl Show for SuperNode {
};
Ok(transformed.unwrap_or_else(|| {
- let mut map = StyleMap::new();
- map.set(TextNode::BASELINE, styles.get(Self::BASELINE));
- map.set(TextNode::SIZE, styles.get(Self::SIZE));
- body.styled_with_map(map)
+ body.styled(TextNode::set_baseline(Self::baseline_in(styles)))
+ .styled(TextNode::set_size(Self::size_in(styles)))
}))
}
}
@@ -174,7 +170,7 @@ fn search_text(content: &Content, sub: bool) -> Option<EcoString> {
/// given string.
fn is_shapable(vt: &Vt, text: &str, styles: StyleChain) -> bool {
let world = vt.world();
- for family in styles.get(TextNode::FONT).0.iter() {
+ for family in TextNode::font_in(styles) {
if let Some(font) = world
.book()
.select(family.as_str(), variant(styles))
diff --git a/library/src/visualize/image.rs b/library/src/visualize/image.rs
index fa5b70ad..1bc864e5 100644
--- a/library/src/visualize/image.rs
+++ b/library/src/visualize/image.rs
@@ -90,7 +90,7 @@ impl Layout for ImageNode {
};
// Compute the actual size of the fitted image.
- let fit = styles.get(Self::FIT);
+ let fit = Self::fit_in(styles);
let fitted = match fit {
ImageFit::Cover | ImageFit::Contain => {
if wide == (fit == ImageFit::Contain) {
diff --git a/library/src/visualize/line.rs b/library/src/visualize/line.rs
index 6fc7fc19..6beb7beb 100644
--- a/library/src/visualize/line.rs
+++ b/library/src/visualize/line.rs
@@ -84,7 +84,7 @@ impl Layout for LineNode {
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
- let stroke = styles.get(Self::STROKE).unwrap_or_default();
+ let stroke = Self::stroke_in(styles).unwrap_or_default();
let origin = self
.start()
diff --git a/library/src/visualize/shape.rs b/library/src/visualize/shape.rs
index b3b9364f..fc1d462e 100644
--- a/library/src/visualize/shape.rs
+++ b/library/src/visualize/shape.rs
@@ -167,11 +167,11 @@ impl Layout for RectNode {
ShapeKind::Rect,
&self.body(),
Axes::new(self.width(), self.height()),
- styles.get(Self::FILL),
- styles.get(Self::STROKE),
- styles.get(Self::INSET),
- styles.get(Self::OUTSET),
- styles.get(Self::RADIUS),
+ Self::fill_in(styles),
+ Self::stroke_in(styles),
+ Self::inset_in(styles),
+ Self::outset_in(styles),
+ Self::radius_in(styles),
)
}
}
@@ -301,11 +301,11 @@ impl Layout for SquareNode {
ShapeKind::Square,
&self.body(),
Axes::new(self.width(), self.height()),
- styles.get(Self::FILL),
- styles.get(Self::STROKE),
- styles.get(Self::INSET),
- styles.get(Self::OUTSET),
- styles.get(Self::RADIUS),
+ Self::fill_in(styles),
+ Self::stroke_in(styles),
+ Self::inset_in(styles),
+ Self::outset_in(styles),
+ Self::radius_in(styles),
)
}
}
@@ -394,10 +394,10 @@ impl Layout for EllipseNode {
ShapeKind::Ellipse,
&self.body(),
Axes::new(self.width(), self.height()),
- styles.get(Self::FILL),
- styles.get(Self::STROKE).map(Sides::splat),
- styles.get(Self::INSET),
- styles.get(Self::OUTSET),
+ Self::fill_in(styles),
+ Self::stroke_in(styles).map(Sides::splat),
+ Self::inset_in(styles),
+ Self::outset_in(styles),
Corners::splat(Rel::zero()),
)
}
@@ -522,10 +522,10 @@ impl Layout for CircleNode {
ShapeKind::Circle,
&self.body(),
Axes::new(self.width(), self.height()),
- styles.get(Self::FILL),
- styles.get(Self::STROKE).map(Sides::splat),
- styles.get(Self::INSET),
- styles.get(Self::OUTSET),
+ Self::fill_in(styles),
+ Self::stroke_in(styles).map(Sides::splat),
+ Self::inset_in(styles),
+ Self::outset_in(styles),
Corners::splat(Rel::zero()),
)
}
diff --git a/macros/src/node.rs b/macros/src/node.rs
index 8a6660ca..a7298841 100644
--- a/macros/src/node.rs
+++ b/macros/src/node.rs
@@ -20,7 +20,9 @@ struct Field {
attrs: Vec<syn::Attribute>,
vis: syn::Visibility,
ident: Ident,
+ ident_in: Ident,
with_ident: Ident,
+ set_ident: Ident,
name: String,
positional: bool,
@@ -36,6 +38,7 @@ struct Field {
skip: bool,
ty: syn::Type,
+ output: syn::Type,
default: Option<syn::Expr>,
}
@@ -62,20 +65,17 @@ fn prepare(stream: TokenStream, body: &syn::ItemStruct) -> Result<Node> {
let mut fields = vec![];
for field in &named.named {
- let Some(mut ident) = field.ident.clone() else {
+ let Some(ident) = field.ident.clone() else {
bail!(field, "expected named field");
};
let mut attrs = field.attrs.clone();
- let settable = has_attr(&mut attrs, "settable");
- if settable {
- ident = Ident::new(&ident.to_string().to_uppercase(), ident.span());
- }
-
- let field = Field {
+ let mut field = Field {
vis: field.vis.clone(),
ident: ident.clone(),
+ ident_in: Ident::new(&format!("{}_in", ident), ident.span()),
with_ident: Ident::new(&format!("with_{}", ident), ident.span()),
+ set_ident: Ident::new(&format!("set_{}", ident), ident.span()),
name: kebab_case(&ident),
positional: has_attr(&mut attrs, "positional"),
@@ -88,12 +88,13 @@ fn prepare(stream: TokenStream, body: &syn::ItemStruct) -> Result<Node> {
Some(ident) => Shorthand::Named(ident),
}),
- settable,
+ settable: has_attr(&mut attrs, "settable"),
fold: has_attr(&mut attrs, "fold"),
resolve: has_attr(&mut attrs, "resolve"),
skip: has_attr(&mut attrs, "skip"),
ty: field.ty.clone(),
+ output: field.ty.clone(),
default: parse_attr(&mut attrs, "default")?.map(|opt| {
opt.unwrap_or_else(|| parse_quote! { ::std::default::Default::default() })
}),
@@ -104,6 +105,15 @@ fn prepare(stream: TokenStream, body: &syn::ItemStruct) -> Result<Node> {
},
};
+ if field.resolve {
+ let output = &field.output;
+ field.output = parse_quote! { <#output as ::typst::model::Resolve>::Output };
+ }
+ if field.fold {
+ let output = &field.output;
+ field.output = parse_quote! { <#output as ::typst::model::Fold>::Output };
+ }
+
if !field.positional && !field.named && !field.variadic && !field.settable {
bail!(ident, "expected positional, named, variadic, or settable");
}
@@ -140,34 +150,22 @@ fn create(node: &Node) -> TokenStream {
let attrs = &node.attrs;
let vis = &node.vis;
let ident = &node.ident;
- let name = &node.name;
+
+ // Inherent methods and functions.
let new = create_new_func(node);
+ let field_methods = node.inherent().map(create_field_method);
+ let with_fields_methods = node.inherent().map(create_with_field_method);
+ let field_in_methods = node.settable().map(create_field_in_method);
+ let field_style_methods = node.settable().map(create_field_style_method);
+
+ // Trait implementations.
let construct = node
.capable
.iter()
.all(|capability| capability != "Construct")
.then(|| create_construct_impl(node));
let set = create_set_impl(node);
- let builders = node.inherent().map(create_builder_method);
- let accessors = node.inherent().map(create_accessor_method);
- let vtable = create_vtable(node);
-
- let mut modules = vec![];
- let mut items = vec![];
- let scope = quote::format_ident!("__{}_keys", ident);
-
- for field in node.settable() {
- let ident = &field.ident;
- let attrs = &field.attrs;
- let vis = &field.vis;
- let ty = &field.ty;
- modules.push(create_field_module(node, field));
- items.push(quote! {
- #(#attrs)*
- #vis const #ident: #scope::#ident::Key<#ty>
- = #scope::#ident::Key(::std::marker::PhantomData);
- });
- }
+ let node = create_node_impl(node);
quote! {
#(#attrs)*
@@ -178,7 +176,10 @@ fn create(node: &Node) -> TokenStream {
impl #ident {
#new
- #(#builders)*
+ #(#field_methods)*
+ #(#with_fields_methods)*
+ #(#field_in_methods)*
+ #(#field_style_methods)*
/// The node's span.
pub fn span(&self) -> Option<::typst::syntax::Span> {
@@ -186,25 +187,7 @@ fn create(node: &Node) -> TokenStream {
}
}
- impl #ident {
- #(#accessors)*
- #(#items)*
- }
-
- impl ::typst::model::Node for #ident {
- fn id() -> ::typst::model::NodeId {
- static META: ::typst::model::NodeMeta = ::typst::model::NodeMeta {
- name: #name,
- vtable: #vtable,
- };
- ::typst::model::NodeId::from_meta(&META)
- }
-
- fn pack(self) -> ::typst::model::Content {
- self.0
- }
- }
-
+ #node
#construct
#set
@@ -213,12 +196,6 @@ fn create(node: &Node) -> TokenStream {
value.0.into()
}
}
-
- #[allow(non_snake_case)]
- mod #scope {
- use super::*;
- #(#modules)*
- }
}
}
@@ -252,25 +229,152 @@ fn create_new_func(node: &Node) -> TokenStream {
}
}
+/// Create an accessor methods for a field.
+fn create_field_method(field: &Field) -> TokenStream {
+ let Field { attrs, vis, ident, name, ty, .. } = field;
+ quote! {
+ #(#attrs)*
+ #vis fn #ident(&self) -> #ty {
+ self.0.cast_field(#name)
+ }
+ }
+}
+
/// Create a builder pattern method for a field.
-fn create_builder_method(field: &Field) -> TokenStream {
- let Field { with_ident, ident, name, ty, .. } = field;
+fn create_with_field_method(field: &Field) -> TokenStream {
+ let Field { vis, ident, with_ident, name, ty, .. } = field;
let doc = format!("Set the [`{}`](Self::{}) field.", name, ident);
quote! {
#[doc = #doc]
- pub fn #with_ident(mut self, #ident: #ty) -> Self {
+ #vis fn #with_ident(mut self, #ident: #ty) -> Self {
Self(self.0.with_field(#name, #ident))
}
}
}
-/// Create an accessor methods for a field.
-fn create_accessor_method(field: &Field) -> TokenStream {
- let Field { attrs, vis, ident, name, ty, .. } = field;
+/// Create a style chain access method for a field.
+fn create_field_in_method(field: &Field) -> TokenStream {
+ let Field { vis, ident_in, name, ty, output, default, .. } = field;
+
+ let doc = format!("Access the `{}` field in the given style chain.", name);
+ let args = quote! { ::typst::model::NodeId::of::<Self>(), #name };
+
+ let body = if field.fold && field.resolve {
+ quote! {
+ fn next(
+ mut values: impl ::std::iter::Iterator<Item = #ty>,
+ styles: ::typst::model::StyleChain,
+ ) -> #output {
+ values
+ .next()
+ .map(|value| {
+ ::typst::model::Fold::fold(
+ ::typst::model::Resolve::resolve(value, styles),
+ next(values, styles),
+ )
+ })
+ .unwrap_or_else(|| #default)
+ }
+ next(styles.properties(#args), styles)
+ }
+ } else if field.fold {
+ quote! {
+ fn next(
+ mut values: impl ::std::iter::Iterator<Item = #ty>,
+ styles: ::typst::model::StyleChain,
+ ) -> #output {
+ values
+ .next()
+ .map(|value| {
+ ::typst::model::Fold::fold(value, next(values, styles))
+ })
+ .unwrap_or_else(|| #default)
+ }
+ next(styles.properties(#args), styles)
+ }
+ } else if field.resolve {
+ quote! {
+ ::typst::model::Resolve::resolve(
+ styles.property::<#ty>(#args).unwrap_or_else(|| #default),
+ styles
+ )
+ }
+ } else {
+ quote! {
+ styles.property(#args).unwrap_or_else(|| #default)
+ }
+ };
+
quote! {
- #(#attrs)*
- #vis fn #ident(&self) -> #ty {
- self.0.cast_field(#name)
+ #[doc = #doc]
+ #[allow(unused)]
+ #vis fn #ident_in(styles: ::typst::model::StyleChain) -> #output {
+ #body
+ }
+ }
+}
+
+/// Create a style creation method for a field.
+fn create_field_style_method(field: &Field) -> TokenStream {
+ let Field { vis, ident, set_ident, name, ty, .. } = field;
+ let doc = format!("Create a style property for the `{}` field.", name);
+ quote! {
+ #[doc = #doc]
+ #vis fn #set_ident(#ident: #ty) -> ::typst::model::Property {
+ ::typst::model::Property::new(
+ ::typst::model::NodeId::of::<Self>(),
+ #name.into(),
+ #ident.into()
+ )
+ }
+ }
+}
+
+/// Create the node's `Node` implementation.
+fn create_node_impl(node: &Node) -> TokenStream {
+ let ident = &node.ident;
+ let name = &node.name;
+ let vtable_func = create_vtable_func(node);
+ quote! {
+ impl ::typst::model::Node for #ident {
+ fn id() -> ::typst::model::NodeId {
+ static META: ::typst::model::NodeMeta = ::typst::model::NodeMeta {
+ name: #name,
+ vtable: #vtable_func,
+ };
+ ::typst::model::NodeId::from_meta(&META)
+ }
+
+ fn pack(self) -> ::typst::model::Content {
+ self.0
+ }
+ }
+ }
+}
+
+/// Create the node's metadata vtable.
+fn create_vtable_func(node: &Node) -> TokenStream {
+ let ident = &node.ident;
+ let checks =
+ node.capable
+ .iter()
+ .filter(|&ident| ident != "Construct")
+ .map(|capability| {
+ quote! {
+ if id == ::std::any::TypeId::of::<dyn #capability>() {
+ return Some(unsafe {
+ ::typst::util::fat::vtable(&
+ Self(::typst::model::Content::new::<#ident>()) as &dyn #capability
+ )
+ });
+ }
+ }
+ });
+
+ quote! {
+ |id| {
+ #(#checks)*
+ None
}
}
}
@@ -353,8 +457,8 @@ fn create_set_impl(node: &Node) -> TokenStream {
.settable()
.filter(|field| !field.skip)
.map(|field| {
- let ident = &field.ident;
let name = &field.name;
+ let set_ident = &field.set_ident;
let value = match &field.shorthand {
Some(Shorthand::Positional) => quote! { args.named_or_find(#name)? },
Some(Shorthand::Named(named)) => {
@@ -364,7 +468,7 @@ fn create_set_impl(node: &Node) -> TokenStream {
None => quote! { args.named(#name)? },
};
- quote! { styles.set_opt(Self::#ident, #value); }
+ quote! { styles.set_opt(#value.map(Self::#set_ident)); }
})
.collect();
@@ -417,123 +521,3 @@ fn create_set_impl(node: &Node) -> TokenStream {
}
}
}
-
-/// Create the module for a single field.
-fn create_field_module(node: &Node, field: &Field) -> TokenStream {
- let node_ident = &node.ident;
- let ident = &field.ident;
- let name = &field.name;
- let ty = &field.ty;
- let default = &field.default;
-
- let mut output = quote! { #ty };
- if field.resolve {
- output = quote! { <#output as ::typst::model::Resolve>::Output };
- }
- if field.fold {
- output = quote! { <#output as ::typst::model::Fold>::Output };
- }
-
- let value = if field.resolve && field.fold {
- quote! {
- values
- .next()
- .map(|value| {
- ::typst::model::Fold::fold(
- ::typst::model::Resolve::resolve(value, chain),
- Self::get(chain, values),
- )
- })
- .unwrap_or(#default)
- }
- } else if field.resolve {
- quote! {
- ::typst::model::Resolve::resolve(
- values.next().unwrap_or(#default),
- chain
- )
- }
- } else if field.fold {
- quote! {
- values
- .next()
- .map(|value| {
- ::typst::model::Fold::fold(
- value,
- Self::get(chain, values),
- )
- })
- .unwrap_or(#default)
- }
- } else {
- quote! {
- values.next().unwrap_or(#default)
- }
- };
-
- // Generate the contents of the module.
- let scope = quote! {
- use super::*;
-
- pub struct Key<T>(pub ::std::marker::PhantomData<T>);
- impl ::std::marker::Copy for Key<#ty> {}
- impl ::std::clone::Clone for Key<#ty> {
- fn clone(&self) -> Self { *self }
- }
-
- impl ::typst::model::Key for Key<#ty> {
- type Value = #ty;
- type Output = #output;
-
- fn id() -> ::typst::model::KeyId {
- static META: ::typst::model::KeyMeta = ::typst::model::KeyMeta {
- name: #name,
- };
- ::typst::model::KeyId::from_meta(&META)
- }
-
- fn node() -> ::typst::model::NodeId {
- ::typst::model::NodeId::of::<#node_ident>()
- }
-
- fn get(
- chain: ::typst::model::StyleChain,
- mut values: impl ::std::iter::Iterator<Item = Self::Value>,
- ) -> Self::Output {
- #value
- }
- }
- };
-
- // Generate the module code.
- quote! {
- pub mod #ident { #scope }
- }
-}
-
-/// Create the node's metadata vtable.
-fn create_vtable(node: &Node) -> TokenStream {
- let ident = &node.ident;
- let checks =
- node.capable
- .iter()
- .filter(|&ident| ident != "Construct")
- .map(|capability| {
- quote! {
- if id == ::std::any::TypeId::of::<dyn #capability>() {
- return Some(unsafe {
- ::typst::util::fat::vtable(&
- Self(::typst::model::Content::new::<#ident>()) as &dyn #capability
- )
- });
- }
- }
- });
-
- quote! {
- |id| {
- #(#checks)*
- None
- }
- }
-}
diff --git a/src/doc.rs b/src/doc.rs
index 0c01dcd2..ce6f4c96 100644
--- a/src/doc.rs
+++ b/src/doc.rs
@@ -274,7 +274,7 @@ impl Frame {
if self.is_empty() {
return;
}
- for meta in styles.get(MetaNode::DATA) {
+ for meta in MetaNode::data_in(styles) {
if matches!(meta, Meta::Hidden) {
self.clear();
break;
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index 8180f11d..0e0828e3 100644
--- a/src/eval/mod.rs
+++ b/src/eval/mod.rs
@@ -354,7 +354,7 @@ fn eval_markup(
}
let tail = eval_markup(vm, exprs)?;
- seq.push(tail.styled_with_recipe(vm.world, recipe)?)
+ seq.push(tail.apply_recipe(vm.world, recipe)?)
}
expr => match expr.eval(vm)? {
Value::Label(label) => {
@@ -783,7 +783,7 @@ fn eval_code(
}
let tail = eval_code(vm, exprs)?.display();
- Value::Content(tail.styled_with_recipe(vm.world, recipe)?)
+ Value::Content(tail.apply_recipe(vm.world, recipe)?)
}
_ => expr.eval(vm)?,
};
diff --git a/src/model/content.rs b/src/model/content.rs
index 1b87aaea..05c5d430 100644
--- a/src/model/content.rs
+++ b/src/model/content.rs
@@ -7,7 +7,7 @@ use std::ops::{Add, AddAssign};
use comemo::Tracked;
use ecow::{EcoString, EcoVec};
-use super::{node, Guard, Key, Property, Recipe, Style, StyleMap};
+use super::{node, Guard, Recipe, Style, StyleMap};
use crate::diag::{SourceResult, StrResult};
use crate::eval::{cast_from_value, Args, Cast, ParamInfo, Value, Vm};
use crate::syntax::Span;
@@ -66,14 +66,9 @@ impl Content {
self.with_field("label", label)
}
- /// Style this content with a single style property.
- pub fn styled<K: Key>(self, key: K, value: K::Value) -> Self {
- self.styled_with_entry(Style::Property(Property::new(key, value)))
- }
-
/// Style this content with a style entry.
- pub fn styled_with_entry(self, style: Style) -> Self {
- self.styled_with_map(style.into())
+ pub fn styled(self, style: impl Into<Style>) -> Self {
+ self.styled_with_map(style.into().into())
}
/// Style this content with a full style map.
@@ -90,7 +85,7 @@ impl Content {
}
/// Style this content with a recipe, eagerly applying it if possible.
- pub fn styled_with_recipe(
+ pub fn apply_recipe(
self,
world: Tracked<dyn World>,
recipe: Recipe,
@@ -98,7 +93,7 @@ impl Content {
if recipe.selector.is_none() {
recipe.apply(world, self)
} else {
- Ok(self.styled_with_entry(Style::Recipe(recipe)))
+ Ok(self.styled(Style::Recipe(recipe)))
}
}
@@ -135,6 +130,7 @@ impl Content {
}
}
+ /// Attach a field to the content.
pub fn with_field(
mut self,
name: impl Into<EcoString>,
@@ -154,6 +150,7 @@ impl Content {
}
}
+ /// Access a field on the content.
pub fn field(&self, name: &str) -> Option<&Value> {
static NONE: Value = Value::None;
self.fields
@@ -163,10 +160,6 @@ impl Content {
.or_else(|| (name == "label").then(|| &NONE))
}
- pub fn fields(&self) -> &[(EcoString, Value)] {
- &self.fields
- }
-
#[track_caller]
pub fn cast_field<T: Cast>(&self, name: &str) -> T {
match self.field(name) {
@@ -175,6 +168,11 @@ impl Content {
}
}
+ /// List all fields on the content.
+ pub fn fields(&self) -> &[(EcoString, Value)] {
+ &self.fields
+ }
+
/// Whether the contained node is of type `T`.
pub fn is<T>(&self) -> bool
where
diff --git a/src/model/styles.rs b/src/model/styles.rs
index cbf4cfb2..bd7a062f 100644
--- a/src/model/styles.rs
+++ b/src/model/styles.rs
@@ -1,8 +1,5 @@
-use std::any::Any;
use std::fmt::{self, Debug, Formatter};
-use std::hash::{Hash, Hasher};
use std::iter;
-use std::marker::PhantomData;
use comemo::Tracked;
use ecow::EcoString;
@@ -33,15 +30,13 @@ impl StyleMap {
/// If the property needs folding and the value is already contained in the
/// style map, `self` contributes the outer values and `value` is the inner
/// one.
- pub fn set<K: Key>(&mut self, key: K, value: K::Value) {
- self.0.push(Style::Property(Property::new(key, value)));
+ pub fn set(&mut self, property: Property) {
+ self.0.push(Style::Property(property));
}
/// Set an inner value for a style property if it is `Some(_)`.
- pub fn set_opt<K: Key>(&mut self, key: K, value: Option<K::Value>) {
- if let Some(value) = value {
- self.set(key, value);
- }
+ pub fn set_opt(&mut self, property: Option<Property>) {
+ self.0.extend(property.map(Style::Property));
}
/// Remove the style that was last set.
@@ -49,14 +44,6 @@ impl StyleMap {
self.0.pop();
}
- /// Whether the map contains a style property for the given key.
- pub fn contains<K: Key>(&self, _: K) -> bool {
- self.0
- .iter()
- .filter_map(|entry| entry.property())
- .any(|property| property.is::<K>())
- }
-
/// Apply outer styles. Like [`chain`](StyleChain::chain), but in-place.
pub fn apply(&mut self, outer: Self) {
self.0.splice(0..0, outer.0.iter().cloned());
@@ -75,7 +62,7 @@ impl StyleMap {
pub fn scoped(mut self) -> Self {
for entry in &mut self.0 {
if let Style::Property(property) = entry {
- property.make_scoped();
+ property.scoped = true;
}
}
self
@@ -163,37 +150,37 @@ impl Debug for Style {
}
}
+impl From<Property> for Style {
+ fn from(property: Property) -> Self {
+ Self::Property(property)
+ }
+}
+
/// A style property originating from a set rule or constructor.
#[derive(Clone, Hash)]
pub struct Property {
- /// The id of the property's [key](Key).
- key: KeyId,
/// The id of the node the property belongs to.
node: NodeId,
+ /// The property's name.
+ name: EcoString,
+ /// The property's value.
+ value: Value,
/// Whether the property should only affect the first node down the
/// hierarchy. Used by constructors.
scoped: bool,
- /// The property's value.
- value: Value,
/// The span of the set rule the property stems from.
origin: Option<Span>,
}
impl Property {
/// Create a new property from a key-value pair.
- pub fn new<K: Key>(_: K, value: K::Value) -> Self {
- Self {
- key: KeyId::of::<K>(),
- node: K::node(),
- value: value.into(),
- scoped: false,
- origin: None,
- }
+ pub fn new(node: NodeId, name: EcoString, value: Value) -> Self {
+ Self { node, name, value, scoped: false, origin: None }
}
- /// Whether this property has the given key.
- pub fn is<K: Key>(&self) -> bool {
- self.key == KeyId::of::<K>()
+ /// Whether this property is the given one.
+ pub fn is(&self, node: NodeId, name: &str) -> bool {
+ self.node == node && self.name == name
}
/// Whether this property belongs to the node with the given id.
@@ -201,37 +188,24 @@ impl Property {
self.node == node
}
- /// Access the property's value if it is of the given key.
+ /// Access the property's value as the given type.
#[track_caller]
- pub fn cast<K: Key>(&self) -> Option<K::Value> {
- if self.key == KeyId::of::<K>() {
- Some(self.value.clone().cast().unwrap_or_else(|err| {
- panic!("{} (for {} with value {:?})", err, self.key.name(), self.value)
- }))
- } else {
- None
- }
- }
-
- /// The node this property is for.
- pub fn node(&self) -> NodeId {
- self.node
- }
-
- /// Whether the property is scoped.
- pub fn scoped(&self) -> bool {
- self.scoped
- }
-
- /// Make the property scoped.
- pub fn make_scoped(&mut self) {
- self.scoped = true;
+ pub fn cast<T: Cast>(&self) -> T {
+ self.value.clone().cast().unwrap_or_else(|err| {
+ panic!(
+ "{} (for {}.{} with value {:?})",
+ err,
+ self.node.name(),
+ self.name,
+ self.value
+ )
+ })
}
}
impl Debug for Property {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- write!(f, "#set {}({}: {:?})", self.node.name(), self.key.name(), self.value)?;
+ write!(f, "#set {}({}: {:?})", self.node.name(), self.name, self.value)?;
if self.scoped {
write!(f, " [scoped]")?;
}
@@ -239,92 +213,6 @@ impl Debug for Property {
}
}
-impl PartialEq for Property {
- fn eq(&self, other: &Self) -> bool {
- self.key == other.key
- && self.value.eq(&other.value)
- && self.scoped == other.scoped
- }
-}
-
-trait Bounds: Debug + Sync + Send + 'static {
- fn as_any(&self) -> &dyn Any;
-}
-
-impl<T> Bounds for T
-where
- T: Debug + Sync + Send + 'static,
-{
- fn as_any(&self) -> &dyn Any {
- self
- }
-}
-
-/// A style property key.
-///
-/// This trait is not intended to be implemented manually.
-pub trait Key: Copy + 'static {
- /// The unfolded type which this property is stored as in a style map.
- type Value: Cast + Into<Value>;
-
- /// The folded type of value that is returned when reading this property
- /// from a style chain.
- type Output;
-
- /// The id of the property.
- fn id() -> KeyId;
-
- /// The id of the node the key belongs to.
- fn node() -> NodeId;
-
- /// Compute an output value from a sequence of values belonging to this key,
- /// folding if necessary.
- fn get(chain: StyleChain, values: impl Iterator<Item = Self::Value>) -> Self::Output;
-}
-
-/// A unique identifier for a style key.
-#[derive(Copy, Clone)]
-pub struct KeyId(&'static KeyMeta);
-
-impl KeyId {
- pub fn of<T: Key>() -> Self {
- T::id()
- }
-
- pub fn from_meta(meta: &'static KeyMeta) -> Self {
- Self(meta)
- }
-
- pub fn name(self) -> &'static str {
- self.0.name
- }
-}
-
-impl Debug for KeyId {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- f.pad(self.name())
- }
-}
-
-impl Hash for KeyId {
- fn hash<H: Hasher>(&self, state: &mut H) {
- state.write_usize(self.0 as *const _ as usize);
- }
-}
-
-impl Eq for KeyId {}
-
-impl PartialEq for KeyId {
- fn eq(&self, other: &Self) -> bool {
- std::ptr::eq(self.0, other.0)
- }
-}
-
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub struct KeyMeta {
- pub name: &'static str,
-}
-
/// A show rule recipe.
#[derive(Clone, Hash)]
pub struct Recipe {
@@ -497,7 +385,7 @@ impl<'a> StyleChain<'a> {
if !self
.entries()
.filter_map(Style::property)
- .any(|p| p.scoped() && *id == p.node())
+ .any(|p| p.scoped && *id == p.node)
{
return *self;
}
@@ -509,27 +397,39 @@ impl<'a> StyleChain<'a> {
}
}
- /// Get the output value of a style property.
- ///
- /// Returns the property's default value if no map in the chain contains an
- /// entry for it. Also takes care of resolving and folding and returns
- /// references where applicable.
- pub fn get<K: Key>(self, key: K) -> K::Output {
- K::get(self, self.values(key))
- }
-
/// Iterate over all style recipes in the chain.
pub fn recipes(self) -> impl Iterator<Item = &'a Recipe> {
self.entries().filter_map(Style::recipe)
}
+ /// Cast the first value for the given property in the chain.
+ pub fn property<T: Cast>(self, node: NodeId, name: &'a str) -> Option<T> {
+ self.properties(node, name).next()
+ }
+
/// Iterate over all values for the given property in the chain.
- fn values<K: Key>(self, _: K) -> Values<'a, K> {
- Values {
- entries: self.entries(),
- key: PhantomData,
- barriers: 0,
- }
+ pub fn properties<T: Cast>(
+ self,
+ node: NodeId,
+ name: &'a str,
+ ) -> impl Iterator<Item = T> + '_ {
+ let mut barriers = 0;
+ self.entries().filter_map(move |entry| {
+ match entry {
+ Style::Property(property) => {
+ if property.is(node, name) {
+ if !property.scoped || barriers <= 1 {
+ return Some(property.cast());
+ }
+ }
+ }
+ Style::Barrier(id) => {
+ barriers += (*id == node) as usize;
+ }
+ _ => {}
+ }
+ None
+ })
}
/// Iterate over the entries of the chain.
@@ -610,38 +510,6 @@ impl<'a> Iterator for Links<'a> {
}
}
-/// An iterator over the values in a style chain.
-struct Values<'a, K> {
- entries: Entries<'a>,
- key: PhantomData<K>,
- barriers: usize,
-}
-
-impl<'a, K: Key> Iterator for Values<'a, K> {
- type Item = K::Value;
-
- #[track_caller]
- fn next(&mut self) -> Option<Self::Item> {
- for entry in &mut self.entries {
- match entry {
- Style::Property(property) => {
- if let Some(value) = property.cast::<K>() {
- if !property.scoped() || self.barriers <= 1 {
- return Some(value);
- }
- }
- }
- Style::Barrier(id) => {
- self.barriers += (*id == K::node()) as usize;
- }
- _ => {}
- }
- }
-
- None
- }
-}
-
/// A sequence of items with associated styles.
#[derive(Clone, Hash)]
pub struct StyleVec<T> {
diff --git a/tests/src/tests.rs b/tests/src/tests.rs
index a86715ef..b35ddcce 100644
--- a/tests/src/tests.rs
+++ b/tests/src/tests.rs
@@ -178,11 +178,13 @@ fn library() -> Library {
// Set page width to 120pt with 10pt margins, so that the inner page is
// exactly 100pt wide. Page height is unbounded and font size is 10pt so
// that it multiplies to nice round numbers.
- lib.styles.set(PageNode::WIDTH, Smart::Custom(Abs::pt(120.0).into()));
- lib.styles.set(PageNode::HEIGHT, Smart::Auto);
lib.styles
- .set(PageNode::MARGIN, Sides::splat(Some(Smart::Custom(Abs::pt(10.0).into()))));
- lib.styles.set(TextNode::SIZE, TextSize(Abs::pt(10.0).into()));
+ .set(PageNode::set_width(Smart::Custom(Abs::pt(120.0).into())));
+ lib.styles.set(PageNode::set_height(Smart::Auto));
+ lib.styles.set(PageNode::set_margin(Sides::splat(Some(Smart::Custom(
+ Abs::pt(10.0).into(),
+ )))));
+ lib.styles.set(TextNode::set_size(TextSize(Abs::pt(10.0).into())));
// Hook up helpers into the global scope.
lib.global.scope_mut().def_func::<TestFunc>("test");