From bd304b99e5d2d23027d90eaae871fdd3bdd12f5d Mon Sep 17 00:00:00 2001 From: Laurenz Date: Tue, 28 Dec 2021 13:37:02 +0100 Subject: [PATCH] Tidying --- src/eval/ops.rs | 4 -- src/library/align.rs | 40 ++++++++------ src/library/columns.rs | 62 +++++++++------------- src/library/flow.rs | 2 + src/library/grid.rs | 88 +++++++++++++++---------------- src/library/heading.rs | 2 + src/library/image.rs | 14 ++--- src/library/link.rs | 2 + src/library/list.rs | 2 + src/library/mod.rs | 111 +++++++++++++++++++++------------------ src/library/pad.rs | 2 + src/library/page.rs | 103 ++++++++++++++++++------------------ src/library/par.rs | 47 +++++++++-------- src/library/placed.rs | 2 + src/library/shape.rs | 2 + src/library/sized.rs | 2 + src/library/spacing.rs | 2 + src/library/stack.rs | 14 ++--- src/library/text.rs | 86 +++++++++++++++--------------- src/library/transform.rs | 4 +- src/library/utility.rs | 6 ++- 21 files changed, 312 insertions(+), 285 deletions(-) diff --git a/src/eval/ops.rs b/src/eval/ops.rs index 23530c101..94e577182 100644 --- a/src/eval/ops.rs +++ b/src/eval/ops.rs @@ -94,10 +94,6 @@ pub fn add(lhs: Value, rhs: Value) -> StrResult { if let (Some(&a), Some(&b)) = (a.downcast::(), b.downcast::()) { - dynamic! { - Spec: "2d alignment", - } - return if a.axis() != b.axis() { Ok(Dyn(Dynamic::new(match a.axis() { SpecAxis::Horizontal => Spec { x: a, y: b }, diff --git a/src/library/align.rs b/src/library/align.rs index c16277f67..16c449053 100644 --- a/src/library/align.rs +++ b/src/library/align.rs @@ -1,9 +1,11 @@ +//! Aligning nodes in their parent container. + use super::prelude::*; use super::ParNode; /// `align`: Configure the alignment along the layouting axes. pub fn align(_: &mut EvalContext, args: &mut Args) -> TypResult { - let aligns: Spec<_> = args.expect("alignment")?; + let aligns: Spec<_> = args.find().unwrap_or_default(); let body: Node = args.expect("body")?; let mut styles = Styles::new(); @@ -16,6 +18,26 @@ pub fn align(_: &mut EvalContext, args: &mut Args) -> TypResult { )) } +dynamic! { + Align: "alignment", +} + +dynamic! { + Spec: "2d alignment", +} + +castable! { + Spec>, + Expected: "1d or 2d alignment", + @align: Align => { + let mut aligns = Spec::default(); + aligns.set(align.axis(), Some(*align)); + aligns + }, + @aligns: Spec => aligns.map(Some), + +} + /// A node that aligns its child. #[derive(Debug, Hash)] pub struct AlignNode { @@ -57,19 +79,3 @@ impl Layout for AlignNode { frames } } - -dynamic! { - Align: "alignment", -} - -castable! { - Spec>, - Expected: "1d or 2d alignment", - @align: Align => { - let mut aligns = Spec::default(); - aligns.set(align.axis(), Some(*align)); - aligns - }, - @aligns: Spec => aligns.map(Some), - -} diff --git a/src/library/columns.rs b/src/library/columns.rs index 25d6da9d7..df55fb521 100644 --- a/src/library/columns.rs +++ b/src/library/columns.rs @@ -1,7 +1,9 @@ +//! Multi-column layouts. + use super::prelude::*; use super::ParNode; -/// `columns`: Stack children along an axis. +/// `columns`: Set content into multiple columns. pub fn columns(_: &mut EvalContext, args: &mut Args) -> TypResult { let columns = args.expect("column count")?; let gutter = args.named("gutter")?.unwrap_or(Relative::new(0.04).into()); @@ -36,6 +38,8 @@ impl Layout for ColumnsNode { ctx: &mut LayoutContext, regions: &Regions, ) -> Vec>> { + let columns = self.columns.get(); + // Separating the infinite space into infinite columns does not make // much sense. Note that this line assumes that no infinitely wide // region will follow if the first region's width is finite. @@ -51,21 +55,15 @@ impl Layout for ColumnsNode { // `region.backlog` and `regions.last`. let mut sizes = vec![]; - let columns = self.columns.get(); - for (current, base) in regions .iter() - .take(1 + regions.backlog.len() + if regions.last.is_some() { 1 } else { 0 }) + .take(1 + regions.backlog.len() + regions.last.iter().len()) { let gutter = self.gutter.resolve(base.x); + let width = (current.x - gutter * (columns - 1) as f64) / columns as f64; + let size = Size::new(width, current.y); gutters.push(gutter); - let size = Size::new( - (current.x - gutter * (columns - 1) as f64) / columns as f64, - current.y, - ); - for _ in 0 .. columns { - sizes.push(size); - } + sizes.extend(std::iter::repeat(size).take(columns)); } let first = sizes.remove(0); @@ -76,24 +74,22 @@ impl Layout for ColumnsNode { ); // Retrieve elements for the last region from the vectors. - let last_gutter = if regions.last.is_some() { + let last_gutter = regions.last.map(|_| { let gutter = gutters.pop().unwrap(); let size = sizes.drain(sizes.len() - columns ..).next().unwrap(); pod.last = Some(size); - Some(gutter) - } else { - None - }; + gutter + }); pod.backlog = sizes.into_iter(); + let mut finished = vec![]; let mut frames = self.child.layout(ctx, &pod).into_iter(); let dir = ctx.styles.get(ParNode::DIR); - - let mut finished = vec![]; let total_regions = (frames.len() as f32 / columns as f32).ceil() as usize; + // Stitch together the columns for each region. for ((current, base), gutter) in regions .iter() .take(total_regions) @@ -103,41 +99,35 @@ impl Layout for ColumnsNode { // Otherwise its the maximum column height for the frame. In that // case, the frame is first created with zero height and then // resized. - let mut height = if regions.expand.y { current.y } else { Length::zero() }; - let mut frame = Frame::new(Spec::new(regions.current.x, height)); - + let height = if regions.expand.y { current.y } else { Length::zero() }; + let mut output = Frame::new(Spec::new(regions.current.x, height)); let mut cursor = Length::zero(); for _ in 0 .. columns { - let child_frame = match frames.next() { + let frame = match frames.next() { Some(frame) => frame.item, None => break, }; - let width = child_frame.size.x; - if !regions.expand.y { - height.set_max(child_frame.size.y); + output.size.y.set_max(frame.size.y); } - frame.push_frame( - Point::with_x(if dir.is_positive() { - cursor - } else { - regions.current.x - cursor - width - }), - child_frame, - ); + let width = frame.size.x; + let x = if dir.is_positive() { + cursor + } else { + regions.current.x - cursor - width + }; + output.push_frame(Point::with_x(x), frame); cursor += width + gutter; } - frame.size.y = height; - let mut cts = Constraints::new(regions.expand); cts.base = base.map(Some); cts.exact = current.map(Some); - finished.push(frame.constrain(cts)); + finished.push(output.constrain(cts)); } finished diff --git a/src/library/flow.rs b/src/library/flow.rs index 6bfa3ddd9..810e566ae 100644 --- a/src/library/flow.rs +++ b/src/library/flow.rs @@ -1,3 +1,5 @@ +//! A flow of paragraphs and other block-level nodes. + use std::fmt::{self, Debug, Formatter}; use super::prelude::*; diff --git a/src/library/grid.rs b/src/library/grid.rs index d341cf5b8..3292cfe0d 100644 --- a/src/library/grid.rs +++ b/src/library/grid.rs @@ -1,47 +1,20 @@ +//! Layout along a row and column raster. + use super::prelude::*; /// `grid`: Arrange children into a grid. pub fn grid(_: &mut EvalContext, args: &mut Args) -> TypResult { - castable! { - Vec, - Expected: "integer or (auto, linear, fractional, or array thereof)", - Value::Auto => vec![TrackSizing::Auto], - Value::Length(v) => vec![TrackSizing::Linear(v.into())], - Value::Relative(v) => vec![TrackSizing::Linear(v.into())], - Value::Linear(v) => vec![TrackSizing::Linear(v)], - Value::Fractional(v) => vec![TrackSizing::Fractional(v)], - Value::Int(v) => vec![TrackSizing::Auto; Value::Int(v).cast()?], - Value::Array(values) => values - .into_iter() - .filter_map(|v| v.cast().ok()) - .collect(), - } - - castable! { - TrackSizing, - Expected: "auto, linear, or fractional", - Value::Auto => Self::Auto, - Value::Length(v) => Self::Linear(v.into()), - Value::Relative(v) => Self::Linear(v.into()), - Value::Linear(v) => Self::Linear(v), - Value::Fractional(v) => Self::Fractional(v), - } - let columns = args.named("columns")?.unwrap_or_default(); let rows = args.named("rows")?.unwrap_or_default(); - let tracks = Spec::new(columns, rows); - let base_gutter: Vec = args.named("gutter")?.unwrap_or_default(); let column_gutter = args.named("column-gutter")?; let row_gutter = args.named("row-gutter")?; - let gutter = Spec::new( - column_gutter.unwrap_or_else(|| base_gutter.clone()), - row_gutter.unwrap_or(base_gutter), - ); - Ok(Value::block(GridNode { - tracks, - gutter, + tracks: Spec::new(columns, rows), + gutter: Spec::new( + column_gutter.unwrap_or_else(|| base_gutter.clone()), + row_gutter.unwrap_or(base_gutter), + ), children: args.all().map(Node::into_block).collect(), })) } @@ -57,17 +30,6 @@ pub struct GridNode { pub children: Vec, } -/// Defines how to size a grid cell along an axis. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub enum TrackSizing { - /// Fit the cell to its contents. - Auto, - /// A length stated in absolute values and/or relative to the parent's size. - Linear(Linear), - /// A length that is the fraction of the remaining free space in the parent. - Fractional(Fractional), -} - impl Layout for GridNode { fn layout( &self, @@ -85,6 +47,42 @@ impl Layout for GridNode { } } +/// Defines how to size a grid cell along an axis. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum TrackSizing { + /// Fit the cell to its contents. + Auto, + /// A length stated in absolute values and/or relative to the parent's size. + Linear(Linear), + /// A length that is the fraction of the remaining free space in the parent. + Fractional(Fractional), +} + +castable! { + Vec, + Expected: "integer or (auto, linear, fractional, or array thereof)", + Value::Auto => vec![TrackSizing::Auto], + Value::Length(v) => vec![TrackSizing::Linear(v.into())], + Value::Relative(v) => vec![TrackSizing::Linear(v.into())], + Value::Linear(v) => vec![TrackSizing::Linear(v)], + Value::Fractional(v) => vec![TrackSizing::Fractional(v)], + Value::Int(v) => vec![TrackSizing::Auto; Value::Int(v).cast()?], + Value::Array(values) => values + .into_iter() + .filter_map(|v| v.cast().ok()) + .collect(), +} + +castable! { + TrackSizing, + Expected: "auto, linear, or fractional", + Value::Auto => Self::Auto, + Value::Length(v) => Self::Linear(v.into()), + Value::Relative(v) => Self::Linear(v.into()), + Value::Linear(v) => Self::Linear(v), + Value::Fractional(v) => Self::Fractional(v), +} + /// Performs grid layout. struct GridLayouter<'a> { /// The children of the grid. diff --git a/src/library/heading.rs b/src/library/heading.rs index 96ff2688b..a0b787f34 100644 --- a/src/library/heading.rs +++ b/src/library/heading.rs @@ -1,3 +1,5 @@ +//! Document-structuring section headings. + use super::prelude::*; use super::{FontFamily, TextNode}; diff --git a/src/library/image.rs b/src/library/image.rs index efb246a1c..4e5acdc65 100644 --- a/src/library/image.rs +++ b/src/library/image.rs @@ -1,3 +1,5 @@ +//! Raster and vector graphics. + use std::io; use super::prelude::*; @@ -106,6 +108,12 @@ pub enum ImageFit { Stretch, } +impl Default for ImageFit { + fn default() -> Self { + Self::Cover + } +} + castable! { ImageFit, Expected: "string", @@ -116,9 +124,3 @@ castable! { _ => Err(r#"expected "cover", "contain" or "stretch""#)?, }, } - -impl Default for ImageFit { - fn default() -> Self { - Self::Cover - } -} diff --git a/src/library/link.rs b/src/library/link.rs index 40604a62d..0f3417936 100644 --- a/src/library/link.rs +++ b/src/library/link.rs @@ -1,3 +1,5 @@ +//! Hyperlinking. + use super::prelude::*; use crate::util::EcoString; diff --git a/src/library/list.rs b/src/library/list.rs index 25eb36009..f407d8e92 100644 --- a/src/library/list.rs +++ b/src/library/list.rs @@ -1,3 +1,5 @@ +//! Unordered (bulleted) and ordered (numbered) lists. + use std::hash::Hash; use super::prelude::*; diff --git a/src/library/mod.rs b/src/library/mod.rs index 1c97f5297..61d3ccc04 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -3,44 +3,25 @@ //! Call [`new`] to obtain a [`Scope`] containing all standard library //! definitions. -mod align; -mod columns; -mod flow; -mod grid; -mod heading; -mod image; -mod link; -mod list; -mod pad; -mod page; -mod par; -mod placed; -mod shape; -mod sized; -mod spacing; -mod stack; -mod text; -mod transform; -mod utility; - -/// Helpful imports for creating library functionality. -mod prelude { - pub use std::fmt::{self, Debug, Formatter}; - pub use std::num::NonZeroUsize; - pub use std::rc::Rc; - - pub use typst_macros::properties; - - pub use crate::diag::{At, TypResult}; - 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::*; - pub use crate::syntax::{Span, Spanned}; - pub use crate::util::{EcoString, OptionExt}; -} +pub mod align; +pub mod columns; +pub mod flow; +pub mod grid; +pub mod heading; +pub mod image; +pub mod link; +pub mod list; +pub mod pad; +pub mod page; +pub mod par; +pub mod placed; +pub mod shape; +pub mod sized; +pub mod spacing; +pub mod stack; +pub mod text; +pub mod transform; +pub mod utility; pub use self::image::*; pub use align::*; @@ -62,8 +43,37 @@ pub use text::*; pub use transform::*; pub use utility::*; -use crate::eval::{Scope, Value}; -use crate::geom::*; +macro_rules! prelude { + ($($reexport:item)*) => { + /// Helpful imports for creating library functionality. + pub mod prelude { + $(#[doc(no_inline)] $reexport)* + } + }; +} + +prelude! { + pub use std::fmt::{self, Debug, Formatter}; + pub use std::num::NonZeroUsize; + pub use std::rc::Rc; + + pub use typst_macros::properties; + + pub use crate::diag::{At, TypResult}; + 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::{ + Constrain, Constrained, Constraints, Layout, LayoutContext, PackedNode, Regions, + }; + pub use crate::syntax::{Span, Spanned}; + pub use crate::util::{EcoString, OptionExt}; +} + +use crate::eval::Scope; +use prelude::*; /// Construct a scope containing all standard library definitions. pub fn new() -> Scope { @@ -78,9 +88,8 @@ pub fn new() -> Scope { std.def_class::>("enum"); // Text functions. - // TODO(style): These should be classes, once that works for inline nodes. - std.def_func("strike", strike); std.def_func("underline", underline); + std.def_func("strike", strike); std.def_func("overline", overline); std.def_func("link", link); @@ -93,8 +102,6 @@ pub fn new() -> Scope { std.def_func("v", v); // Layout functions. - // TODO(style): Decide which of these should be classes - // (and which of their properties should be settable). std.def_func("box", box_); std.def_func("block", block); std.def_func("stack", stack); @@ -160,12 +167,6 @@ dynamic! { Dir: "direction", } -castable! { - Paint, - Expected: "color", - Value::Color(color) => Paint::Solid(color), -} - castable! { usize, Expected: "non-negative integer", @@ -173,14 +174,20 @@ castable! { } castable! { - prelude::NonZeroUsize, + NonZeroUsize, Expected: "positive integer", Value::Int(int) => int .try_into() - .and_then(|n: usize| n.try_into()) + .and_then(usize::try_into) .map_err(|_| "must be positive")?, } +castable! { + Paint, + Expected: "color", + Value::Color(color) => Paint::Solid(color), +} + castable! { String, Expected: "string", diff --git a/src/library/pad.rs b/src/library/pad.rs index 75fea2e57..70d88b412 100644 --- a/src/library/pad.rs +++ b/src/library/pad.rs @@ -1,3 +1,5 @@ +//! Surrounding nodes with extra space. + use super::prelude::*; /// `pad`: Pad content at the sides. diff --git a/src/library/page.rs b/src/library/page.rs index 100b4d0c5..00f2dfc6a 100644 --- a/src/library/page.rs +++ b/src/library/page.rs @@ -1,4 +1,4 @@ -#![allow(unused)] +//! Pages of paper. use std::fmt::{self, Display, Formatter}; use std::str::FromStr; @@ -6,11 +6,6 @@ use std::str::FromStr; use super::prelude::*; use super::{ColumnsNode, PadNode}; -/// `pagebreak`: Start a new page. -pub fn pagebreak(_: &mut EvalContext, _: &mut Args) -> TypResult { - Ok(Value::Node(Node::Pagebreak)) -} - /// Layouts its child onto one or multiple pages. #[derive(Clone, PartialEq, Hash)] pub struct PageNode { @@ -42,7 +37,7 @@ impl PageNode { pub const FILL: Option = None; /// How many columns the page has. pub const COLUMNS: NonZeroUsize = NonZeroUsize::new(1).unwrap(); - /// How many columns the page has. + /// How much space is between the page's columns. pub const COLUMN_GUTTER: Linear = Relative::new(0.04).into(); } @@ -74,11 +69,12 @@ impl Set for PageNode { } let margins = args.named("margins")?; - styles.set_opt(Self::FLIPPED, args.named("flipped")?); styles.set_opt(Self::LEFT, args.named("left")?.or(margins)); styles.set_opt(Self::TOP, args.named("top")?.or(margins)); styles.set_opt(Self::RIGHT, args.named("right")?.or(margins)); styles.set_opt(Self::BOTTOM, args.named("bottom")?.or(margins)); + + styles.set_opt(Self::FLIPPED, args.named("flipped")?); styles.set_opt(Self::FILL, args.named("fill")?); styles.set_opt(Self::COLUMNS, args.named("columns")?); styles.set_opt(Self::COLUMN_GUTTER, args.named("column-gutter")?); @@ -163,6 +159,36 @@ impl Debug for PageNode { } } +/// `pagebreak`: Start a new page. +pub fn pagebreak(_: &mut EvalContext, _: &mut Args) -> TypResult { + Ok(Value::Node(Node::Pagebreak)) +} + +/// Defines default margins for a class of related papers. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum PaperClass { + Custom, + Base, + US, + Newspaper, + Book, +} + +impl PaperClass { + /// The default margins for this page class. + fn default_margins(self) -> Sides { + let f = |r| Relative::new(r).into(); + let s = |l, t, r, b| Sides::new(f(l), f(t), f(r), f(b)); + match self { + Self::Custom => s(0.1190, 0.0842, 0.1190, 0.0842), + Self::Base => s(0.1190, 0.0842, 0.1190, 0.0842), + Self::US => s(0.1760, 0.1092, 0.1760, 0.0910), + Self::Newspaper => s(0.0455, 0.0587, 0.0455, 0.0294), + Self::Book => s(0.1200, 0.0852, 0.1500, 0.0965), + } + } +} + /// Specification of a paper. #[derive(Debug, Copy, Clone)] pub struct Paper { @@ -197,37 +223,6 @@ 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 { - Custom, - Base, - US, - Newspaper, - Book, -} - -impl PaperClass { - /// The default margins for this page class. - fn default_margins(self) -> Sides { - let f = |r| Relative::new(r).into(); - let s = |l, t, r, b| Sides::new(f(l), f(t), f(r), f(b)); - match self { - Self::Custom => s(0.1190, 0.0842, 0.1190, 0.0842), - Self::Base => s(0.1190, 0.0842, 0.1190, 0.0842), - Self::US => s(0.1760, 0.1092, 0.1760, 0.0910), - Self::Newspaper => s(0.0455, 0.0587, 0.0455, 0.0294), - Self::Book => s(0.1200, 0.0852, 0.1500, 0.0965), - } - } -} - /// Defines paper constants and a paper parsing implementation. macro_rules! papers { ($(($var:ident: $class:ident, $width:expr, $height: expr, $($pats:tt)*))*) => { @@ -252,18 +247,6 @@ macro_rules! papers { } } } - - /// The error when parsing a [`Paper`] from a string fails. - #[derive(Debug, Copy, Clone, Eq, PartialEq)] - pub struct ParsePaperError; - - impl Display for ParsePaperError { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - f.pad("invalid paper name") - } - } - - impl std::error::Error for ParsePaperError {} }; } @@ -421,3 +404,21 @@ papers! { (PRESENTATION_16_9: Base, 297.0, 167.0625, "presentation-16-9") (PRESENTATION_4_3: Base, 280.0, 210.0, "presentation-4-3") } + +castable! { + Paper, + Expected: "string", + Value::Str(string) => Paper::from_str(&string).map_err(|e| e.to_string())?, +} + +/// The error when parsing a [`Paper`] from a string fails. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct ParsePaperError; + +impl Display for ParsePaperError { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.pad("invalid paper name") + } +} + +impl std::error::Error for ParsePaperError {} diff --git a/src/library/par.rs b/src/library/par.rs index 26280d8ec..2c12f9f64 100644 --- a/src/library/par.rs +++ b/src/library/par.rs @@ -1,3 +1,5 @@ +//! Paragraph layout. + use std::fmt::{self, Debug, Formatter}; use std::rc::Rc; @@ -9,16 +11,6 @@ use super::prelude::*; use super::{shape, ShapedText, SpacingKind, SpacingNode, TextNode}; use crate::util::{EcoString, RangeExt, RcExt, SliceExt}; -/// `parbreak`: Start a new paragraph. -pub fn parbreak(_: &mut EvalContext, _: &mut Args) -> TypResult { - Ok(Value::Node(Node::Parbreak)) -} - -/// `linebreak`: Start a new line. -pub fn linebreak(_: &mut EvalContext, _: &mut Args) -> TypResult { - Ok(Value::Node(Node::Linebreak)) -} - /// A node that arranges its children into a paragraph. #[derive(Hash)] pub struct ParNode(pub Vec); @@ -62,17 +54,17 @@ impl Set for ParNode { dir = Some(v); } - let mut align = None; - if let Some(Spanned { v, span }) = args.named::>("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 }); - } + let align = + if let Some(Spanned { v, span }) = args.named::>("align")? { + if v.axis() != SpecAxis::Horizontal { + bail!(span, "must be horizontal"); + } + Some(v) + } else if let Some(dir) = dir { + Some(if dir == Dir::LTR { Align::Left } else { Align::Right }) + } else { + None + }; styles.set_opt(Self::DIR, dir); styles.set_opt(Self::ALIGN, align); @@ -107,8 +99,7 @@ impl Layout for ParNode { impl ParNode { /// Concatenate all text in the paragraph into one string, replacing spacing /// with a space character and other non-text nodes with the object - /// replacement character. Returns the full text alongside the range each - /// child spans in the text. + /// replacement character. fn collect_text(&self) -> String { let mut text = String::new(); for string in self.strings() { @@ -190,6 +181,16 @@ impl Debug for ParChild { } } +/// `parbreak`: Start a new paragraph. +pub fn parbreak(_: &mut EvalContext, _: &mut Args) -> TypResult { + Ok(Value::Node(Node::Parbreak)) +} + +/// `linebreak`: Start a new line. +pub fn linebreak(_: &mut EvalContext, _: &mut Args) -> TypResult { + Ok(Value::Node(Node::Linebreak)) +} + /// A paragraph representation in which children are already layouted and text /// is separated into shapable runs. struct ParLayouter<'a> { diff --git a/src/library/placed.rs b/src/library/placed.rs index 589a299bc..bdf3e2cd3 100644 --- a/src/library/placed.rs +++ b/src/library/placed.rs @@ -1,3 +1,5 @@ +//! Absolute placement of nodes. + use super::prelude::*; use super::AlignNode; diff --git a/src/library/shape.rs b/src/library/shape.rs index a9c9b3333..3eb1a794a 100644 --- a/src/library/shape.rs +++ b/src/library/shape.rs @@ -1,3 +1,5 @@ +//! Colorable geometrical shapes. + use std::f64::consts::SQRT_2; use super::prelude::*; diff --git a/src/library/sized.rs b/src/library/sized.rs index 6d677ca83..49b515cfb 100644 --- a/src/library/sized.rs +++ b/src/library/sized.rs @@ -1,3 +1,5 @@ +//! Horizontal and vertical sizing of nodes. + use super::prelude::*; /// `box`: Size content and place it into a paragraph. diff --git a/src/library/spacing.rs b/src/library/spacing.rs index b5ecce693..e552828ce 100644 --- a/src/library/spacing.rs +++ b/src/library/spacing.rs @@ -1,3 +1,5 @@ +//! Horizontal and vertical spacing between nodes. + use super::prelude::*; /// `h`: Horizontal spacing. diff --git a/src/library/stack.rs b/src/library/stack.rs index 8a1f0fd59..dfe5e2455 100644 --- a/src/library/stack.rs +++ b/src/library/stack.rs @@ -1,3 +1,5 @@ +//! Side-by-side layout of nodes along an axis. + use std::fmt::{self, Debug, Formatter}; use super::prelude::*; @@ -215,17 +217,17 @@ impl<'a> StackLayouter<'a> { } let mut output = Frame::new(size); - let mut before = Length::zero(); + let mut cursor = Length::zero(); let mut ruler: Align = self.stack.dir.start().into(); // Place all frames. for item in self.items.drain(..) { match item { StackItem::Absolute(v) => { - before += v; + cursor += v; } StackItem::Fractional(v) => { - before += v.resolve(self.fr, remaining); + cursor += v.resolve(self.fr, remaining); } StackItem::Frame(frame, align) => { if self.stack.dir.is_positive() { @@ -239,13 +241,13 @@ impl<'a> StackLayouter<'a> { let child = frame.size.get(self.axis); let block = ruler.resolve(parent - self.used.main) + if self.stack.dir.is_positive() { - before + cursor } else { - self.used.main - child - before + self.used.main - child - cursor }; let pos = Gen::new(Length::zero(), block).to_point(self.axis); - before += child; + cursor += child; output.push_frame(pos, frame); } } diff --git a/src/library/text.rs b/src/library/text.rs index 99c68f795..df71fec9e 100644 --- a/src/library/text.rs +++ b/src/library/text.rs @@ -1,3 +1,5 @@ +//! Text shaping and styling. + use std::borrow::Cow; use std::convert::TryInto; use std::fmt::{self, Debug, Formatter}; @@ -15,33 +17,6 @@ use crate::font::{ use crate::geom::{Dir, Em, Length, Point, Size}; use crate::util::{EcoString, SliceExt}; -/// `strike`: Typeset striken-through text. -pub fn strike(_: &mut EvalContext, args: &mut Args) -> TypResult { - line_impl(args, LineKind::Strikethrough) -} - -/// `underline`: Typeset underlined text. -pub fn underline(_: &mut EvalContext, args: &mut Args) -> TypResult { - line_impl(args, LineKind::Underline) -} - -/// `overline`: Typeset text with an overline. -pub fn overline(_: &mut EvalContext, args: &mut Args) -> TypResult { - line_impl(args, LineKind::Overline) -} - -fn line_impl(args: &mut Args, kind: LineKind) -> TypResult { - let stroke = args.named("stroke")?.or_else(|| args.find()); - let thickness = args.named::("thickness")?.or_else(|| args.find()); - let offset = args.named("offset")?; - let extent = args.named("extent")?.unwrap_or_default(); - let body: Node = args.expect("body")?; - let deco = LineDecoration { kind, stroke, thickness, offset, extent }; - Ok(Value::Node( - body.styled(Styles::one(TextNode::LINES, vec![deco])), - )) -} - /// A single run of text with the same style. #[derive(Hash)] pub struct TextNode { @@ -216,6 +191,21 @@ impl Debug for FontFamily { } } +dynamic! { + FontFamily: "font family", + Value::Str(string) => Self::named(&string), +} + +castable! { + Vec, + Expected: "string, generic family or array thereof", + Value::Str(string) => vec![FontFamily::named(&string)], + Value::Array(values) => { + values.into_iter().filter_map(|v| v.cast().ok()).collect() + }, + @family: FontFamily => vec![family.clone()], +} + /// A specific font family like "Arial". #[derive(Clone, Eq, PartialEq, Hash)] pub struct NamedFamily(String); @@ -238,21 +228,6 @@ impl Debug for NamedFamily { } } -dynamic! { - FontFamily: "font family", - Value::Str(string) => Self::named(&string), -} - -castable! { - Vec, - Expected: "string, generic family or array thereof", - Value::Str(string) => vec![FontFamily::named(&string)], - Value::Array(values) => { - values.into_iter().filter_map(|v| v.cast().ok()).collect() - }, - @family: FontFamily => vec![family.clone()], -} - castable! { Vec, Expected: "string or array of strings", @@ -421,6 +396,33 @@ castable! { .collect(), } +/// `strike`: Typeset striken-through text. +pub fn strike(_: &mut EvalContext, args: &mut Args) -> TypResult { + line_impl(args, LineKind::Strikethrough) +} + +/// `underline`: Typeset underlined text. +pub fn underline(_: &mut EvalContext, args: &mut Args) -> TypResult { + line_impl(args, LineKind::Underline) +} + +/// `overline`: Typeset text with an overline. +pub fn overline(_: &mut EvalContext, args: &mut Args) -> TypResult { + line_impl(args, LineKind::Overline) +} + +fn line_impl(args: &mut Args, kind: LineKind) -> TypResult { + let stroke = args.named("stroke")?.or_else(|| args.find()); + let thickness = args.named::("thickness")?.or_else(|| args.find()); + let offset = args.named("offset")?; + let extent = args.named("extent")?.unwrap_or_default(); + let body: Node = args.expect("body")?; + let deco = LineDecoration { kind, stroke, thickness, offset, extent }; + Ok(Value::Node( + body.styled(Styles::one(TextNode::LINES, vec![deco])), + )) +} + /// Defines a line that is positioned over, under or on top of text. #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct LineDecoration { diff --git a/src/library/transform.rs b/src/library/transform.rs index 6a6b40348..ef468d7b4 100644 --- a/src/library/transform.rs +++ b/src/library/transform.rs @@ -1,3 +1,5 @@ +//! Affine transformations on nodes. + use super::prelude::*; use crate::geom::Transform; @@ -20,7 +22,7 @@ pub fn scale(_: &mut EvalContext, args: &mut Args) -> TypResult { /// `rotate`: Rotate content without affecting layout. pub fn rotate(_: &mut EvalContext, args: &mut Args) -> TypResult { - let angle = args.expect("angle")?; + let angle = args.named("angle")?.or_else(|| args.find()).unwrap_or_default(); let transform = Transform::rotation(angle); transform_impl(args, transform) } diff --git a/src/library/utility.rs b/src/library/utility.rs index b2435e8a1..4e4632c45 100644 --- a/src/library/utility.rs +++ b/src/library/utility.rs @@ -1,3 +1,5 @@ +//! Computational utility functions. + use std::cmp::Ordering; use std::str::FromStr; @@ -44,7 +46,7 @@ pub fn join(_: &mut EvalContext, args: &mut Args) -> TypResult { Ok(result) } -/// `int`: Try to convert a value to a integer. +/// `int`: Convert a value to a integer. pub fn int(_: &mut EvalContext, args: &mut Args) -> TypResult { let Spanned { v, span } = args.expect("value")?; Ok(Value::Int(match v { @@ -59,7 +61,7 @@ pub fn int(_: &mut EvalContext, args: &mut Args) -> TypResult { })) } -/// `float`: Try to convert a value to a float. +/// `float`: Convert a value to a float. pub fn float(_: &mut EvalContext, args: &mut Args) -> TypResult { let Spanned { v, span } = args.expect("value")?; Ok(Value::Float(match v {