diff options
Diffstat (limited to 'src/library')
| -rw-r--r-- | src/library/elements.rs | 49 | ||||
| -rw-r--r-- | src/library/layout.rs | 169 | ||||
| -rw-r--r-- | src/library/mod.rs | 1 | ||||
| -rw-r--r-- | src/library/text.rs | 114 |
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)) } |
