summaryrefslogtreecommitdiff
path: root/src/library
diff options
context:
space:
mode:
Diffstat (limited to 'src/library')
-rw-r--r--src/library/elements.rs49
-rw-r--r--src/library/layout.rs169
-rw-r--r--src/library/mod.rs1
-rw-r--r--src/library/text.rs114
4 files changed, 173 insertions, 160 deletions
diff --git a/src/library/elements.rs b/src/library/elements.rs
index f4577084..f90363bb 100644
--- a/src/library/elements.rs
+++ b/src/library/elements.rs
@@ -23,9 +23,11 @@ pub fn image(ctx: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
})
})?;
- Ok(Value::template(move |ctx| {
- ctx.inline(ImageNode { id, width, height })
- }))
+ Ok(Value::Template(Template::from_inline(move |_| ImageNode {
+ id,
+ width,
+ height,
+ })))
}
/// `rect`: A rectangle with optional content.
@@ -61,22 +63,23 @@ fn rect_impl(
fill: Option<Color>,
body: Template,
) -> Value {
- Value::template(move |ctx| {
- let mut stack = ctx.exec_template(&body);
+ Value::Template(Template::from_inline(move |state| {
+ let mut stack = body.to_stack(state);
stack.aspect = aspect;
- let fixed = FixedNode { width, height, child: stack.into() };
+ let mut node = FixedNode { width, height, child: stack.into() }.into();
if let Some(fill) = fill {
- ctx.inline(BackgroundNode {
+ node = BackgroundNode {
shape: BackgroundShape::Rect,
fill: Paint::Color(fill),
- child: fixed.into(),
- });
- } else {
- ctx.inline(fixed);
+ child: node,
+ }
+ .into();
}
- })
+
+ node
+ }))
}
/// `ellipse`: An ellipse with optional content.
@@ -112,15 +115,15 @@ fn ellipse_impl(
fill: Option<Color>,
body: Template,
) -> Value {
- Value::template(move |ctx| {
+ Value::Template(Template::from_inline(move |state| {
// This padding ratio ensures that the rectangular padded region fits
// perfectly into the ellipse.
const PAD: f64 = 0.5 - SQRT_2 / 4.0;
- let mut stack = ctx.exec_template(&body);
+ let mut stack = body.to_stack(state);
stack.aspect = aspect;
- let fixed = FixedNode {
+ let mut node = FixedNode {
width,
height,
child: PadNode {
@@ -128,16 +131,18 @@ fn ellipse_impl(
child: stack.into(),
}
.into(),
- };
+ }
+ .into();
if let Some(fill) = fill {
- ctx.inline(BackgroundNode {
+ node = BackgroundNode {
shape: BackgroundShape::Ellipse,
fill: Paint::Color(fill),
- child: fixed.into(),
- });
- } else {
- ctx.inline(fixed);
+ child: node,
+ }
+ .into();
}
- })
+
+ node
+ }))
}
diff --git a/src/library/layout.rs b/src/library/layout.rs
index e910139c..b1510cb6 100644
--- a/src/library/layout.rs
+++ b/src/library/layout.rs
@@ -3,7 +3,7 @@ use crate::layout::{FixedNode, GridNode, PadNode, StackChild, StackNode, TrackSi
use crate::paper::{Paper, PaperClass};
/// `page`: Configure pages.
-pub fn page(_: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
+pub fn page(ctx: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
let paper = match args.eat::<Spanned<Str>>() {
Some(name) => match Paper::from_name(&name.v) {
None => bail!(name.span, "invalid paper name"),
@@ -20,87 +20,83 @@ pub fn page(_: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
let right = args.named("right")?;
let bottom = args.named("bottom")?;
let flip = args.named("flip")?;
- let body = args.expect::<Template>("body")?;
- Ok(Value::template(move |ctx| {
- let snapshot = ctx.state.clone();
- let state = ctx.state.page_mut();
+ ctx.template.modify(move |state| {
+ let page = state.page_mut();
if let Some(paper) = paper {
- state.class = paper.class();
- state.size = paper.size();
+ page.class = paper.class();
+ page.size = paper.size();
}
if let Some(width) = width {
- state.class = PaperClass::Custom;
- state.size.width = width;
+ page.class = PaperClass::Custom;
+ page.size.width = width;
}
if let Some(height) = height {
- state.class = PaperClass::Custom;
- state.size.height = height;
+ page.class = PaperClass::Custom;
+ page.size.height = height;
}
if let Some(margins) = margins {
- state.margins = Sides::splat(Some(margins));
+ page.margins = Sides::splat(Some(margins));
}
if let Some(left) = left {
- state.margins.left = Some(left);
+ page.margins.left = Some(left);
}
if let Some(top) = top {
- state.margins.top = Some(top);
+ page.margins.top = Some(top);
}
if let Some(right) = right {
- state.margins.right = Some(right);
+ page.margins.right = Some(right);
}
if let Some(bottom) = bottom {
- state.margins.bottom = Some(bottom);
+ page.margins.bottom = Some(bottom);
}
if flip.unwrap_or(false) {
- std::mem::swap(&mut state.size.width, &mut state.size.height);
+ std::mem::swap(&mut page.size.width, &mut page.size.height);
}
+ });
- ctx.pagebreak(false, true);
- body.exec(ctx);
+ ctx.template.pagebreak(false);
- ctx.state = snapshot;
- ctx.pagebreak(true, false);
- }))
+ Ok(Value::None)
}
/// `pagebreak`: Start a new page.
-pub fn pagebreak(_: &mut EvalContext, _: &mut Arguments) -> TypResult<Value> {
- Ok(Value::template(move |ctx| ctx.pagebreak(true, true)))
+pub fn pagebreak(ctx: &mut EvalContext, _: &mut Arguments) -> TypResult<Value> {
+ ctx.template.pagebreak(true);
+ Ok(Value::None)
}
/// `h`: Horizontal spacing.
-pub fn h(_: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
+pub fn h(ctx: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
let spacing = args.expect("spacing")?;
- Ok(Value::template(move |ctx| {
- ctx.spacing(GenAxis::Cross, spacing);
- }))
+ ctx.template.spacing(GenAxis::Cross, spacing);
+ Ok(Value::None)
}
/// `v`: Vertical spacing.
-pub fn v(_: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
+pub fn v(ctx: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
let spacing = args.expect("spacing")?;
- Ok(Value::template(move |ctx| {
- ctx.spacing(GenAxis::Main, spacing);
- }))
+ ctx.template.spacing(GenAxis::Main, spacing);
+ Ok(Value::None)
}
/// `align`: Configure the alignment along the layouting axes.
-pub fn align(_: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
- let mut horizontal = args.named("horizontal")?;
- let mut vertical = args.named("vertical")?;
+pub fn align(ctx: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
let first = args.eat::<Align>();
let second = args.eat::<Align>();
- let body = args.expect::<Template>("body")?;
+ let body = args.eat::<Template>();
+
+ let mut horizontal = args.named("horizontal")?;
+ let mut vertical = args.named("vertical")?;
for value in first.into_iter().chain(second) {
match value.axis() {
@@ -114,38 +110,52 @@ pub fn align(_: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
}
}
- Ok(Value::template(move |ctx| {
- if let Some(horizontal) = horizontal {
- ctx.state.aligns.cross = horizontal;
- }
+ let realign = |template: &mut Template| {
+ template.modify(move |state| {
+ if let Some(horizontal) = horizontal {
+ state.aligns.cross = horizontal;
+ }
- if let Some(vertical) = vertical {
- ctx.state.aligns.main = vertical;
- ctx.parbreak();
+ if let Some(vertical) = vertical {
+ state.aligns.main = vertical;
+ }
+ });
+
+ if vertical.is_some() {
+ template.parbreak();
}
+ };
- body.exec(ctx);
- }))
+ if let Some(body) = body {
+ let mut template = Template::new();
+ template.save();
+ realign(&mut template);
+ template += body;
+ template.restore();
+ Ok(Value::Template(template))
+ } else {
+ realign(&mut ctx.template);
+ Ok(Value::None)
+ }
}
/// `box`: Place content in a rectangular box.
pub fn boxed(_: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
let width = args.named("width")?;
let height = args.named("height")?;
- let body = args.eat().unwrap_or_default();
- Ok(Value::template(move |ctx| {
- let child = ctx.exec_template(&body).into();
- ctx.inline(FixedNode { width, height, child });
- }))
+ let body: Template = args.eat().unwrap_or_default();
+ Ok(Value::Template(Template::from_inline(move |state| {
+ let child = body.to_stack(state).into();
+ FixedNode { width, height, child }
+ })))
}
/// `block`: Place content in a block.
pub fn block(_: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
- let body = args.expect("body")?;
- Ok(Value::template(move |ctx| {
- let block = ctx.exec_template(&body);
- ctx.block(block);
- }))
+ let body: Template = args.expect("body")?;
+ Ok(Value::Template(Template::from_block(move |state| {
+ body.to_stack(state)
+ })))
}
/// `pad`: Pad content at the sides.
@@ -155,7 +165,7 @@ pub fn pad(_: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
let top = args.named("top")?;
let right = args.named("right")?;
let bottom = args.named("bottom")?;
- let body = args.expect("body")?;
+ let body: Template = args.expect("body")?;
let padding = Sides::new(
left.or(all).unwrap_or_default(),
@@ -164,36 +174,38 @@ pub fn pad(_: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
bottom.or(all).unwrap_or_default(),
);
- Ok(Value::template(move |ctx| {
- let child = ctx.exec_template(&body).into();
- ctx.block(PadNode { padding, child });
- }))
+ Ok(Value::Template(Template::from_block(move |state| {
+ PadNode {
+ padding,
+ child: body.to_stack(&state).into(),
+ }
+ })))
}
/// `stack`: Stack children along an axis.
pub fn stack(_: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
let dir = args.named("dir")?;
- let children: Vec<_> = args.all().collect();
+ let children: Vec<Template> = args.all().collect();
- Ok(Value::template(move |ctx| {
+ Ok(Value::Template(Template::from_block(move |state| {
let children = children
.iter()
.map(|child| {
- let child = ctx.exec_template(child).into();
- StackChild::Any(child, ctx.state.aligns)
+ let child = child.to_stack(state).into();
+ StackChild::Any(child, state.aligns)
})
.collect();
- let mut dirs = Gen::new(None, dir).unwrap_or(ctx.state.dirs);
+ let mut dirs = Gen::new(None, dir).unwrap_or(state.dirs);
// If the directions become aligned, fix up the cross direction since
// that's the one that is not user-defined.
if dirs.main.axis() == dirs.cross.axis() {
- dirs.cross = ctx.state.dirs.main;
+ dirs.cross = state.dirs.main;
}
- ctx.block(StackNode { dirs, aspect: None, children });
- }))
+ StackNode { dirs, aspect: None, children }
+ })))
}
/// `grid`: Arrange children into a grid.
@@ -211,7 +223,7 @@ pub fn grid(_: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
let column_dir = args.named("column-dir")?;
let row_dir = args.named("row-dir")?;
- let children: Vec<_> = args.all().collect();
+ let children: Vec<Template> = args.all().collect();
let tracks = Gen::new(columns, rows);
let gutter = Gen::new(
@@ -219,14 +231,13 @@ pub fn grid(_: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
gutter_rows.unwrap_or(default),
);
- Ok(Value::template(move |ctx| {
+ Ok(Value::Template(Template::from_block(move |state| {
let children =
- children.iter().map(|child| ctx.exec_template(child).into()).collect();
-
- let mut dirs = Gen::new(column_dir, row_dir).unwrap_or(ctx.state.dirs);
+ children.iter().map(|child| child.to_stack(&state).into()).collect();
// If the directions become aligned, try to fix up the direction which
// is not user-defined.
+ let mut dirs = Gen::new(column_dir, row_dir).unwrap_or(state.dirs);
if dirs.main.axis() == dirs.cross.axis() {
let target = if column_dir.is_some() {
&mut dirs.main
@@ -234,20 +245,20 @@ pub fn grid(_: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
&mut dirs.cross
};
- *target = if target.axis() == ctx.state.dirs.cross.axis() {
- ctx.state.dirs.main
+ *target = if target.axis() == state.dirs.cross.axis() {
+ state.dirs.main
} else {
- ctx.state.dirs.cross
+ state.dirs.cross
};
}
- ctx.block(GridNode {
+ GridNode {
dirs,
tracks: tracks.clone(),
gutter: gutter.clone(),
children,
- })
- }))
+ }
+ })))
}
/// Defines size of rows and columns in a grid.
diff --git a/src/library/mod.rs b/src/library/mod.rs
index f1134282..dd1574f3 100644
--- a/src/library/mod.rs
+++ b/src/library/mod.rs
@@ -19,7 +19,6 @@ use std::rc::Rc;
use crate::color::{Color, RgbaColor};
use crate::diag::TypResult;
use crate::eval::{Arguments, EvalContext, Scope, Str, Template, Value};
-use crate::exec::Exec;
use crate::font::{FontFamily, FontStretch, FontStyle, FontWeight, VerticalFontMetric};
use crate::geom::*;
use crate::syntax::Spanned;
diff --git a/src/library/text.rs b/src/library/text.rs
index 9ffb182a..6154885c 100644
--- a/src/library/text.rs
+++ b/src/library/text.rs
@@ -1,10 +1,15 @@
-use crate::exec::{FontState, LineState};
+use crate::eval::{FontState, LineState};
use crate::layout::Paint;
use super::*;
/// `font`: Configure the font.
-pub fn font(_: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
+pub fn font(ctx: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
+ let list = args.named("family")?.or_else(|| {
+ let families: Vec<_> = args.all().collect();
+ (!families.is_empty()).then(|| FontDef(Rc::new(families)))
+ });
+
let size = args.named::<Linear>("size")?.or_else(|| args.eat());
let style = args.named("style")?;
let weight = args.named("weight")?;
@@ -12,67 +17,59 @@ pub fn font(_: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
let top_edge = args.named("top-edge")?;
let bottom_edge = args.named("bottom-edge")?;
let fill = args.named("fill")?;
-
- let list = args.named("family")?.or_else(|| {
- let families: Vec<_> = args.all().collect();
- (!families.is_empty()).then(|| FontDef(Rc::new(families)))
- });
-
let serif = args.named("serif")?;
let sans_serif = args.named("sans-serif")?;
let monospace = args.named("monospace")?;
- let body = args.expect::<Template>("body")?;
-
- Ok(Value::template(move |ctx| {
- let state = ctx.state.font_mut();
+ ctx.template.modify(move |state| {
+ let font = state.font_mut();
if let Some(size) = size {
- state.size = size.resolve(state.size);
+ font.size = size.resolve(font.size);
}
if let Some(style) = style {
- state.variant.style = style;
+ font.variant.style = style;
}
if let Some(weight) = weight {
- state.variant.weight = weight;
+ font.variant.weight = weight;
}
if let Some(stretch) = stretch {
- state.variant.stretch = stretch;
+ font.variant.stretch = stretch;
}
if let Some(top_edge) = top_edge {
- state.top_edge = top_edge;
+ font.top_edge = top_edge;
}
if let Some(bottom_edge) = bottom_edge {
- state.bottom_edge = bottom_edge;
+ font.bottom_edge = bottom_edge;
}
if let Some(fill) = fill {
- state.fill = Paint::Color(fill);
+ font.fill = Paint::Color(fill);
}
if let Some(FontDef(list)) = &list {
- state.families_mut().list = list.clone();
+ font.families_mut().list = list.clone();
}
if let Some(FamilyDef(serif)) = &serif {
- state.families_mut().serif = serif.clone();
+ font.families_mut().serif = serif.clone();
}
if let Some(FamilyDef(sans_serif)) = &sans_serif {
- state.families_mut().sans_serif = sans_serif.clone();
+ font.families_mut().sans_serif = sans_serif.clone();
}
if let Some(FamilyDef(monospace)) = &monospace {
- state.families_mut().monospace = monospace.clone();
+ font.families_mut().monospace = monospace.clone();
}
+ });
- body.exec(ctx);
- }))
+ Ok(Value::None)
}
struct FontDef(Rc<Vec<FontFamily>>);
@@ -104,29 +101,29 @@ castable! {
}
/// `par`: Configure paragraphs.
-pub fn par(_: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
+pub fn par(ctx: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
let par_spacing = args.named("spacing")?;
let line_spacing = args.named("leading")?;
- let body = args.expect::<Template>("body")?;
- Ok(Value::template(move |ctx| {
- let state = ctx.state.par_mut();
+ ctx.template.modify(move |state| {
+ let par = state.par_mut();
if let Some(par_spacing) = par_spacing {
- state.par_spacing = par_spacing;
+ par.par_spacing = par_spacing;
}
if let Some(line_spacing) = line_spacing {
- state.line_spacing = line_spacing;
+ par.line_spacing = line_spacing;
}
+ });
- ctx.parbreak();
- body.exec(ctx);
- }))
+ ctx.template.parbreak();
+
+ Ok(Value::None)
}
/// `lang`: Configure the language.
-pub fn lang(_: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
+pub fn lang(ctx: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
let iso = args.eat::<Str>();
let dir = if let Some(dir) = args.named::<Spanned<Dir>>("dir")? {
if dir.v.axis() == SpecAxis::Horizontal {
@@ -138,16 +135,13 @@ pub fn lang(_: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
iso.as_deref().map(lang_dir)
};
- let body = args.expect::<Template>("body")?;
+ if let Some(dir) = dir {
+ ctx.template.modify(move |state| state.dirs.cross = dir);
+ }
- Ok(Value::template(move |ctx| {
- if let Some(dir) = dir {
- ctx.state.dirs.cross = dir;
- }
+ ctx.template.parbreak();
- ctx.parbreak();
- body.exec(ctx);
- }))
+ Ok(Value::None)
}
/// The default direction for the language identified by the given `iso` code.
@@ -159,22 +153,23 @@ fn lang_dir(iso: &str) -> Dir {
}
}
-/// `strike`: Enable striken-through text.
-pub fn strike(_: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
- line_impl(args, |font| &mut font.strikethrough)
+/// `strike`: Set striken-through text.
+pub fn strike(ctx: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
+ line_impl(ctx, args, |font| &mut font.strikethrough)
}
-/// `underline`: Enable underlined text.
-pub fn underline(_: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
- line_impl(args, |font| &mut font.underline)
+/// `underline`: Set underlined text.
+pub fn underline(ctx: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
+ line_impl(ctx, args, |font| &mut font.underline)
}
-/// `overline`: Add an overline above text.
-pub fn overline(_: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
- line_impl(args, |font| &mut font.overline)
+/// `overline`: Set text with an overline.
+pub fn overline(ctx: &mut EvalContext, args: &mut Arguments) -> TypResult<Value> {
+ line_impl(ctx, args, |font| &mut font.overline)
}
fn line_impl(
+ _: &mut EvalContext,
args: &mut Arguments,
substate: fn(&mut FontState) -> &mut Option<Rc<LineState>>,
) -> TypResult<Value> {
@@ -182,10 +177,10 @@ fn line_impl(
let thickness = args.named::<Linear>("thickness")?.or_else(|| args.eat());
let offset = args.named("offset")?;
let extent = args.named("extent")?.unwrap_or_default();
- let body = args.expect::<Template>("body")?;
+ let body = args.expect("body")?;
// Suppress any existing strikethrough if strength is explicitly zero.
- let state = thickness.map_or(true, |s| !s.is_zero()).then(|| {
+ let line = thickness.map_or(true, |s| !s.is_zero()).then(|| {
Rc::new(LineState {
stroke: stroke.map(Paint::Color),
thickness,
@@ -194,8 +189,11 @@ fn line_impl(
})
});
- Ok(Value::template(move |ctx| {
- *substate(ctx.state.font_mut()) = state.clone();
- body.exec(ctx);
- }))
+ let mut template = Template::new();
+ template.save();
+ template.modify(move |state| *substate(state.font_mut()) = line.clone());
+ template += body;
+ template.restore();
+
+ Ok(Value::Template(template))
}