diff options
| author | Laurenz <laurmaedje@gmail.com> | 2021-12-15 20:27:41 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2021-12-15 20:27:41 +0100 |
| commit | 2a3d0f4b390457174ed09347dd29e97ff9a783e4 (patch) | |
| tree | 0e0634bff6b7f64131267f4cbe05651c1c91d900 /src/library | |
| parent | 244ad386ec271ff86a2101eb4cc38d37a55552b9 (diff) | |
Set Rules Episode VII: The Set Awakens
Diffstat (limited to 'src/library')
| -rw-r--r-- | src/library/mod.rs | 28 | ||||
| -rw-r--r-- | src/library/page.rs | 92 | ||||
| -rw-r--r-- | src/library/par.rs | 88 | ||||
| -rw-r--r-- | src/library/text.rs | 95 |
4 files changed, 156 insertions, 147 deletions
diff --git a/src/library/mod.rs b/src/library/mod.rs index 9b6da6a9..1e2ee224 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -27,7 +27,9 @@ mod prelude { pub use std::rc::Rc; pub use crate::diag::{At, TypResult}; - pub use crate::eval::{Args, EvalContext, Node, Property, Smart, Styles, Value}; + pub use crate::eval::{ + Args, Construct, EvalContext, Node, Property, Set, Smart, Styles, Value, + }; pub use crate::frame::*; pub use crate::geom::*; pub use crate::layout::*; @@ -60,22 +62,24 @@ use crate::geom::*; pub fn new() -> Scope { let mut std = Scope::new(); - // Text. - std.def_func("font", font); - std.def_func("par", par); - std.def_func("parbreak", parbreak); + // Classes. + std.def_class::<PageNode>("page"); + std.def_class::<ParNode>("par"); + std.def_class::<TextNode>("text"); + + // Text functions. std.def_func("strike", strike); std.def_func("underline", underline); std.def_func("overline", overline); std.def_func("link", link); - // Layout. - std.def_func("page", page); - std.def_func("pagebreak", pagebreak); + // Layout functions. std.def_func("h", h); std.def_func("v", v); std.def_func("box", box_); std.def_func("block", block); + std.def_func("pagebreak", pagebreak); + std.def_func("parbreak", parbreak); std.def_func("stack", stack); std.def_func("grid", grid); std.def_func("pad", pad); @@ -85,14 +89,14 @@ pub fn new() -> Scope { std.def_func("scale", scale); std.def_func("rotate", rotate); - // Elements. + // Element functions. std.def_func("image", image); std.def_func("rect", rect); std.def_func("square", square); std.def_func("ellipse", ellipse); std.def_func("circle", circle); - // Utility. + // Utility functions. std.def_func("assert", assert); std.def_func("type", type_); std.def_func("repr", repr); @@ -110,14 +114,14 @@ pub fn new() -> Scope { std.def_func("len", len); std.def_func("sorted", sorted); - // Colors. + // Predefined colors. std.def_const("white", RgbaColor::WHITE); std.def_const("black", RgbaColor::BLACK); std.def_const("eastern", RgbaColor::new(0x23, 0x9D, 0xAD, 0xFF)); std.def_const("conifer", RgbaColor::new(0x9f, 0xEB, 0x52, 0xFF)); std.def_const("forest", RgbaColor::new(0x43, 0xA1, 0x27, 0xFF)); - // Arbitrary constants. + // Other constants. std.def_const("ltr", Dir::LTR); std.def_const("rtl", Dir::RTL); std.def_const("ttb", Dir::TTB); diff --git a/src/library/page.rs b/src/library/page.rs index 4a4fab03..0b0cc2d9 100644 --- a/src/library/page.rs +++ b/src/library/page.rs @@ -6,53 +6,6 @@ use std::str::FromStr; use super::prelude::*; use super::PadNode; -/// `page`: Configure pages. -pub fn page(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> { - castable! { - Paper, - Expected: "string", - Value::Str(string) => Paper::from_str(&string).map_err(|e| e.to_string())?, - } - - let body: Option<Node> = args.find(); - - let mut map = Styles::new(); - let styles = match body { - Some(_) => &mut map, - None => &mut ctx.styles, - }; - - if let Some(paper) = args.named::<Paper>("paper")?.or_else(|| args.find()) { - styles.set(PageNode::CLASS, paper.class()); - styles.set(PageNode::WIDTH, Smart::Custom(paper.width())); - styles.set(PageNode::HEIGHT, Smart::Custom(paper.height())); - } - - if let Some(width) = args.named("width")? { - styles.set(PageNode::CLASS, PaperClass::Custom); - styles.set(PageNode::WIDTH, width); - } - - if let Some(height) = args.named("height")? { - styles.set(PageNode::CLASS, PaperClass::Custom); - styles.set(PageNode::HEIGHT, height); - } - - let margins = args.named("margins")?; - - set!(styles, PageNode::FLIPPED => args.named("flipped")?); - set!(styles, PageNode::LEFT => args.named("left")?.or(margins)); - set!(styles, PageNode::TOP => args.named("top")?.or(margins)); - set!(styles, PageNode::RIGHT => args.named("right")?.or(margins)); - set!(styles, PageNode::BOTTOM => args.named("bottom")?.or(margins)); - set!(styles, PageNode::FILL => args.named("fill")?); - - Ok(match body { - Some(body) => Value::block(body.into_block().styled(map)), - None => Value::None, - }) -} - /// `pagebreak`: Start a new page. pub fn pagebreak(_: &mut EvalContext, _: &mut Args) -> TypResult<Value> { Ok(Value::Node(Node::Pagebreak)) @@ -90,6 +43,45 @@ properties! { FILL: Option<Paint> = None, } +impl Construct for PageNode { + fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Node> { + // TODO(set): Make sure it's really a page so that it doesn't merge + // with adjacent pages. + Ok(Node::Page(args.expect::<Node>("body")?.into_block())) + } +} + +impl Set for PageNode { + fn set(styles: &mut Styles, args: &mut Args) -> TypResult<()> { + if let Some(paper) = args.named::<Paper>("paper")?.or_else(|| args.find()) { + styles.set(PageNode::CLASS, paper.class()); + styles.set(PageNode::WIDTH, Smart::Custom(paper.width())); + styles.set(PageNode::HEIGHT, Smart::Custom(paper.height())); + } + + if let Some(width) = args.named("width")? { + styles.set(PageNode::CLASS, PaperClass::Custom); + styles.set(PageNode::WIDTH, width); + } + + if let Some(height) = args.named("height")? { + styles.set(PageNode::CLASS, PaperClass::Custom); + styles.set(PageNode::HEIGHT, height); + } + + let margins = args.named("margins")?; + + set!(styles, PageNode::FLIPPED => args.named("flipped")?); + set!(styles, PageNode::LEFT => args.named("left")?.or(margins)); + set!(styles, PageNode::TOP => args.named("top")?.or(margins)); + set!(styles, PageNode::RIGHT => args.named("right")?.or(margins)); + set!(styles, PageNode::BOTTOM => args.named("bottom")?.or(margins)); + set!(styles, PageNode::FILL => args.named("fill")?); + + Ok(()) + } +} + impl PageNode { /// Layout the page run into a sequence of frames, one per page. pub fn layout(&self, ctx: &mut LayoutContext) -> Vec<Rc<Frame>> { @@ -182,6 +174,12 @@ impl Default for Paper { } } +castable! { + Paper, + Expected: "string", + Value::Str(string) => Paper::from_str(&string).map_err(|e| e.to_string())?, +} + /// Defines default margins for a class of related papers. #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub enum PaperClass { diff --git a/src/library/par.rs b/src/library/par.rs index 6ea960bf..d63c2315 100644 --- a/src/library/par.rs +++ b/src/library/par.rs @@ -9,46 +9,6 @@ use super::prelude::*; use super::{shape, ShapedText, SpacingKind, SpacingNode, TextNode}; use crate::util::{EcoString, RangeExt, RcExt, SliceExt}; -/// `par`: Configure paragraphs. -pub fn par(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> { - let spacing = args.named("spacing")?; - let leading = args.named("leading")?; - - let mut dir = - args.named("lang")? - .map(|iso: EcoString| match iso.to_lowercase().as_str() { - "ar" | "he" | "fa" | "ur" | "ps" | "yi" => Dir::RTL, - "en" | "fr" | "de" => Dir::LTR, - _ => Dir::LTR, - }); - - if let Some(Spanned { v, span }) = args.named::<Spanned<Dir>>("dir")? { - if v.axis() != SpecAxis::Horizontal { - bail!(span, "must be horizontal"); - } - dir = Some(v); - } - - let mut align = None; - if let Some(Spanned { v, span }) = args.named::<Spanned<Align>>("align")? { - if v.axis() != SpecAxis::Horizontal { - bail!(span, "must be horizontal"); - } - align = Some(v); - } - - if let (Some(dir), None) = (dir, align) { - align = Some(if dir == Dir::LTR { Align::Left } else { Align::Right }); - } - - set!(ctx.styles, ParNode::DIR => dir); - set!(ctx.styles, ParNode::ALIGN => align); - set!(ctx.styles, ParNode::LEADING => leading); - set!(ctx.styles, ParNode::SPACING => spacing); - - Ok(Value::None) -} - /// `parbreak`: Start a new paragraph. pub fn parbreak(_: &mut EvalContext, _: &mut Args) -> TypResult<Value> { Ok(Value::Node(Node::Parbreak)) @@ -71,6 +31,54 @@ properties! { SPACING: Linear = Relative::new(1.2).into(), } +impl Construct for ParNode { + fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Node> { + // Lift to a block so that it doesn't merge with adjacent stuff. + Ok(Node::Block(args.expect::<Node>("body")?.into_block())) + } +} + +impl Set for ParNode { + fn set(styles: &mut Styles, args: &mut Args) -> TypResult<()> { + let spacing = args.named("spacing")?; + let leading = args.named("leading")?; + + let mut dir = + args.named("lang")? + .map(|iso: EcoString| match iso.to_lowercase().as_str() { + "ar" | "he" | "fa" | "ur" | "ps" | "yi" => Dir::RTL, + "en" | "fr" | "de" => Dir::LTR, + _ => Dir::LTR, + }); + + if let Some(Spanned { v, span }) = args.named::<Spanned<Dir>>("dir")? { + if v.axis() != SpecAxis::Horizontal { + bail!(span, "must be horizontal"); + } + dir = Some(v); + } + + let mut align = None; + if let Some(Spanned { v, span }) = args.named::<Spanned<Align>>("align")? { + if v.axis() != SpecAxis::Horizontal { + bail!(span, "must be horizontal"); + } + align = Some(v); + } + + if let (Some(dir), None) = (dir, align) { + align = Some(if dir == Dir::LTR { Align::Left } else { Align::Right }); + } + + set!(styles, ParNode::DIR => dir); + set!(styles, ParNode::ALIGN => align); + set!(styles, ParNode::LEADING => leading); + set!(styles, ParNode::SPACING => spacing); + + Ok(()) + } +} + impl Layout for ParNode { fn layout( &self, diff --git a/src/library/text.rs b/src/library/text.rs index 4ab378c2..4baf4bc5 100644 --- a/src/library/text.rs +++ b/src/library/text.rs @@ -15,54 +15,6 @@ use crate::font::{ use crate::geom::{Dir, Em, Length, Point, Size}; use crate::util::{EcoString, SliceExt}; -/// `font`: Configure the font. -pub fn font(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> { - let body = args.find::<Node>(); - - let mut map = Styles::new(); - let styles = match body { - Some(_) => &mut map, - None => &mut ctx.styles, - }; - - let list = args.named("family")?.or_else(|| { - let families: Vec<_> = args.all().collect(); - (!families.is_empty()).then(|| families) - }); - - set!(styles, TextNode::FAMILY_LIST => list); - set!(styles, TextNode::SERIF_LIST => args.named("serif")?); - set!(styles, TextNode::SANS_SERIF_LIST => args.named("sans-serif")?); - set!(styles, TextNode::MONOSPACE_LIST => args.named("monospace")?); - set!(styles, TextNode::FALLBACK => args.named("fallback")?); - set!(styles, TextNode::STYLE => args.named("style")?); - set!(styles, TextNode::WEIGHT => args.named("weight")?); - set!(styles, TextNode::STRETCH => args.named("stretch")?); - set!(styles, TextNode::FILL => args.named("fill")?.or_else(|| args.find())); - set!(styles, TextNode::SIZE => args.named("size")?.or_else(|| args.find())); - set!(styles, TextNode::TRACKING => args.named("tracking")?.map(Em::new)); - set!(styles, TextNode::TOP_EDGE => args.named("top-edge")?); - set!(styles, TextNode::BOTTOM_EDGE => args.named("bottom-edge")?); - set!(styles, TextNode::KERNING => args.named("kerning")?); - set!(styles, TextNode::SMALLCAPS => args.named("smallcaps")?); - set!(styles, TextNode::ALTERNATES => args.named("alternates")?); - set!(styles, TextNode::STYLISTIC_SET => args.named("stylistic-set")?); - set!(styles, TextNode::LIGATURES => args.named("ligatures")?); - set!(styles, TextNode::DISCRETIONARY_LIGATURES => args.named("discretionary-ligatures")?); - set!(styles, TextNode::HISTORICAL_LIGATURES => args.named("historical-ligatures")?); - set!(styles, TextNode::NUMBER_TYPE => args.named("number-type")?); - set!(styles, TextNode::NUMBER_WIDTH => args.named("number-width")?); - set!(styles, TextNode::NUMBER_POSITION => args.named("number-position")?); - set!(styles, TextNode::SLASHED_ZERO => args.named("slashed-zero")?); - set!(styles, TextNode::FRACTIONS => args.named("fractions")?); - set!(styles, TextNode::FEATURES => args.named("features")?); - - Ok(match body { - Some(body) => Value::Node(body.styled(map)), - None => Value::None, - }) -} - /// `strike`: Typeset striken-through text. pub fn strike(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { line_impl(args, LineKind::Strikethrough) @@ -172,6 +124,53 @@ properties! { FEATURES: Vec<(Tag, u32)> = vec![], } +impl Construct for TextNode { + fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Node> { + // We don't need to do anything more here because the whole point of the + // text constructor is to apply the styles and that happens + // automatically during class construction. + args.expect::<Node>("body") + } +} + +impl Set for TextNode { + fn set(styles: &mut Styles, args: &mut Args) -> TypResult<()> { + let list = args.named("family")?.or_else(|| { + let families: Vec<_> = args.all().collect(); + (!families.is_empty()).then(|| families) + }); + + set!(styles, TextNode::FAMILY_LIST => list); + set!(styles, TextNode::SERIF_LIST => args.named("serif")?); + set!(styles, TextNode::SANS_SERIF_LIST => args.named("sans-serif")?); + set!(styles, TextNode::MONOSPACE_LIST => args.named("monospace")?); + set!(styles, TextNode::FALLBACK => args.named("fallback")?); + set!(styles, TextNode::STYLE => args.named("style")?); + set!(styles, TextNode::WEIGHT => args.named("weight")?); + set!(styles, TextNode::STRETCH => args.named("stretch")?); + set!(styles, TextNode::FILL => args.named("fill")?.or_else(|| args.find())); + set!(styles, TextNode::SIZE => args.named("size")?.or_else(|| args.find())); + set!(styles, TextNode::TRACKING => args.named("tracking")?.map(Em::new)); + set!(styles, TextNode::TOP_EDGE => args.named("top-edge")?); + set!(styles, TextNode::BOTTOM_EDGE => args.named("bottom-edge")?); + set!(styles, TextNode::KERNING => args.named("kerning")?); + set!(styles, TextNode::SMALLCAPS => args.named("smallcaps")?); + set!(styles, TextNode::ALTERNATES => args.named("alternates")?); + set!(styles, TextNode::STYLISTIC_SET => args.named("stylistic-set")?); + set!(styles, TextNode::LIGATURES => args.named("ligatures")?); + set!(styles, TextNode::DISCRETIONARY_LIGATURES => args.named("discretionary-ligatures")?); + set!(styles, TextNode::HISTORICAL_LIGATURES => args.named("historical-ligatures")?); + set!(styles, TextNode::NUMBER_TYPE => args.named("number-type")?); + set!(styles, TextNode::NUMBER_WIDTH => args.named("number-width")?); + set!(styles, TextNode::NUMBER_POSITION => args.named("number-position")?); + set!(styles, TextNode::SLASHED_ZERO => args.named("slashed-zero")?); + set!(styles, TextNode::FRACTIONS => args.named("fractions")?); + set!(styles, TextNode::FEATURES => args.named("features")?); + + Ok(()) + } +} + impl Debug for TextNode { fn fmt(&self, f: &mut Formatter) -> fmt::Result { if f.alternate() { |
