summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-01-03 00:12:09 +0100
committerLaurenz <laurmaedje@gmail.com>2021-01-03 00:12:09 +0100
commitaae67bd572ad86f4c57e364daa51a9dc883b8913 (patch)
tree0aba021e0748ebad2197ea390385ec5f93ccbc6e
parent1c40dc42e7bc7b799b77f06d25414aca59a044ba (diff)
Move and rename many things 🚛
-rw-r--r--bench/src/bench.rs12
-rw-r--r--src/diag.rs21
-rw-r--r--src/env.rs13
-rw-r--r--src/eval/call.rs (renamed from src/eval/args.rs)52
-rw-r--r--src/eval/context.rs298
-rw-r--r--src/eval/mod.rs383
-rw-r--r--src/eval/scope.rs4
-rw-r--r--src/eval/state.rs58
-rw-r--r--src/eval/value.rs22
-rw-r--r--src/export/pdf.rs53
-rw-r--r--src/font.rs15
-rw-r--r--src/geom/align.rs4
-rw-r--r--src/geom/dir.rs4
-rw-r--r--src/geom/gen.rs10
-rw-r--r--src/geom/mod.rs2
-rw-r--r--src/geom/point.rs4
-rw-r--r--src/geom/size.rs4
-rw-r--r--src/geom/spec.rs10
-rw-r--r--src/layout/fixed.rs12
-rw-r--r--src/layout/mod.rs197
-rw-r--r--src/layout/node.rs52
-rw-r--r--src/layout/pad.rs34
-rw-r--r--src/layout/par.rs84
-rw-r--r--src/layout/spacing.rs10
-rw-r--r--src/layout/stack.rs72
-rw-r--r--src/layout/text.rs16
-rw-r--r--src/lib.rs34
-rw-r--r--src/library/insert.rs23
-rw-r--r--src/library/layout.rs45
-rw-r--r--src/main.rs4
-rw-r--r--src/paper.rs4
-rw-r--r--src/parse/collection.rs14
-rw-r--r--src/parse/lines.rs2
-rw-r--r--src/parse/mod.rs52
-rw-r--r--src/parse/resolve.rs2
-rw-r--r--src/parse/scanner.rs4
-rw-r--r--src/parse/tests.rs26
-rw-r--r--src/parse/tokens.rs2
-rw-r--r--src/prelude.rs6
-rw-r--r--src/shaping.rs28
-rw-r--r--src/syntax/expr.rs53
-rw-r--r--src/syntax/ident.rs8
-rw-r--r--src/syntax/mod.rs4
-rw-r--r--src/syntax/node.rs9
-rw-r--r--src/syntax/span.rs14
-rw-r--r--src/syntax/token.rs2
-rw-r--r--tests/typ/example-coma.typ2
-rw-r--r--tests/typeset.rs30
48 files changed, 888 insertions, 926 deletions
diff --git a/bench/src/bench.rs b/bench/src/bench.rs
index 947a02c5..b8093d53 100644
--- a/bench/src/bench.rs
+++ b/bench/src/bench.rs
@@ -33,18 +33,18 @@ fn benchmarks(c: &mut Criterion) {
// Prepare intermediate results and run warm.
let state = State::default();
- let tree = parse(COMA).output;
- let document = eval(&tree, Rc::clone(&env), state.clone()).output;
- let layouts = layout(&document, Rc::clone(&env));
+ let syntax_tree = parse(COMA).output;
+ let layout_tree = eval(&syntax_tree, Rc::clone(&env), state.clone()).output;
+ let frames = layout(&layout_tree, Rc::clone(&env));
// Bench!
bench!("parse-coma": parse(COMA));
- bench!("eval-coma": eval(&tree, Rc::clone(&env), state.clone()));
- bench!("layout-coma": layout(&document, Rc::clone(&env)));
+ bench!("eval-coma": eval(&syntax_tree, Rc::clone(&env), state.clone()));
+ bench!("layout-coma": layout(&layout_tree, Rc::clone(&env)));
bench!("typeset-coma": typeset(COMA, Rc::clone(&env), state.clone()));
let env = env.borrow();
- bench!("export-pdf-coma": pdf::export(&layouts, &env));
+ bench!("export-pdf-coma": pdf::export(&frames, &env));
}
criterion_group!(benches, benchmarks);
diff --git a/src/diag.rs b/src/diag.rs
index 2872ce5a..a7bea6f1 100644
--- a/src/diag.rs
+++ b/src/diag.rs
@@ -1,8 +1,6 @@
//! Diagnostics and decorations for source code.
//!
-//! There are no fatal errors. The document will always compile and yield a
-//! layout on a best effort process, but diagnostics are nevertheless generated
-//! for incorrect things.
+//! Errors are never fatal, the document will always compile and yield a layout.
use crate::syntax::SpanVec;
use std::fmt::{self, Display, Formatter};
@@ -21,22 +19,9 @@ impl<T> Pass<T> {
pub fn new(output: T, feedback: Feedback) -> Self {
Self { output, feedback }
}
-
- /// Create a new pass with empty feedback.
- pub fn okay(output: T) -> Self {
- Self { output, feedback: Feedback::new() }
- }
-
- /// Map the output type and keep the feedback data.
- pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Pass<U> {
- Pass {
- output: f(self.output),
- feedback: self.feedback,
- }
- }
}
-/// Diagnostic and semantic syntax highlighting data.
+/// Diagnostics and semantic syntax highlighting information.
#[derive(Debug, Default, Clone, Eq, PartialEq)]
pub struct Feedback {
/// Diagnostics about the source code.
@@ -64,7 +49,7 @@ impl Feedback {
}
}
-/// A diagnostic that arose in parsing or layouting.
+/// A diagnostic with severity level and message.
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Diag {
diff --git a/src/env.rs b/src/env.rs
index a3e14715..58c44a5f 100644
--- a/src/env.rs
+++ b/src/env.rs
@@ -42,12 +42,13 @@ impl ResourceLoader {
Self { paths: HashMap::new(), entries: vec![] }
}
- /// Load a resource from a path.
- pub fn load<R: 'static>(
- &mut self,
- path: impl AsRef<Path>,
- parse: impl FnOnce(Vec<u8>) -> Option<R>,
- ) -> Option<(ResourceId, &R)> {
+ /// Load a resource from a path and parse it.
+ pub fn load<P, F, R>(&mut self, path: P, parse: F) -> Option<(ResourceId, &R)>
+ where
+ P: AsRef<Path>,
+ F: FnOnce(Vec<u8>) -> Option<R>,
+ R: 'static,
+ {
let path = path.as_ref();
let id = match self.paths.entry(path.to_owned()) {
Entry::Occupied(entry) => *entry.get(),
diff --git a/src/eval/args.rs b/src/eval/call.rs
index ea248ec4..186e7630 100644
--- a/src/eval/args.rs
+++ b/src/eval/call.rs
@@ -1,14 +1,37 @@
use super::*;
+use crate::diag::Deco;
-/// Evaluated arguments to a function.
-#[derive(Debug)]
-pub struct Args {
- span: Span,
- pos: SpanVec<Value>,
- named: Vec<(Spanned<String>, Spanned<Value>)>,
+impl Eval for Spanned<&ExprCall> {
+ type Output = Value;
+
+ fn eval(self, ctx: &mut EvalContext) -> Self::Output {
+ let name = &self.v.name.v;
+ let span = self.v.name.span;
+
+ if let Some(value) = ctx.state.scope.get(name) {
+ if let Value::Func(func) = value {
+ let func = func.clone();
+ ctx.deco(Deco::Resolved.with_span(span));
+
+ let mut args = self.v.args.as_ref().eval(ctx);
+ let returned = func(ctx, &mut args);
+ args.finish(ctx);
+
+ return returned;
+ } else {
+ let ty = value.type_name();
+ ctx.diag(error!(span, "a value of type {} is not callable", ty));
+ }
+ } else if !name.is_empty() {
+ ctx.diag(error!(span, "unknown function"));
+ }
+
+ ctx.deco(Deco::Unresolved.with_span(span));
+ Value::Error
+ }
}
-impl Eval for Spanned<&Arguments> {
+impl Eval for Spanned<&ExprArgs> {
type Output = Args;
fn eval(self, ctx: &mut EvalContext) -> Self::Output {
@@ -33,6 +56,14 @@ impl Eval for Spanned<&Arguments> {
}
}
+/// Evaluated arguments to a function.
+#[derive(Debug)]
+pub struct Args {
+ span: Span,
+ pos: SpanVec<Value>,
+ named: Vec<(Spanned<String>, Spanned<Value>)>,
+}
+
impl Args {
/// Find the first convertible positional argument.
pub fn find<T>(&mut self, ctx: &mut EvalContext) -> Option<T>
@@ -66,9 +97,8 @@ impl Args {
self.pos.iter_mut().filter_map(move |slot| try_cast(ctx, slot))
}
- /// Convert the value for the given named argument.
- ///
- /// Generates an error if the conversion fails.
+ /// Convert the value for the given named argument, producing an error if
+ /// the conversion fails.
pub fn get<'a, T>(&mut self, ctx: &mut EvalContext, name: &str) -> Option<T>
where
T: Cast<Spanned<Value>>,
@@ -78,7 +108,7 @@ impl Args {
cast(ctx, value)
}
- /// Generate "unexpected argument" errors for all remaining arguments.
+ /// Produce "unexpected argument" errors for all remaining arguments.
pub fn finish(self, ctx: &mut EvalContext) {
let a = self.pos.iter().map(|v| v.as_ref());
let b = self.named.iter().map(|(k, v)| (&v.v).with_span(k.span.join(v.span)));
diff --git a/src/eval/context.rs b/src/eval/context.rs
new file mode 100644
index 00000000..ece33146
--- /dev/null
+++ b/src/eval/context.rs
@@ -0,0 +1,298 @@
+use std::any::Any;
+use std::rc::Rc;
+
+use fontdock::FontStyle;
+
+use super::*;
+use crate::diag::Diag;
+use crate::diag::{Deco, Feedback, Pass};
+use crate::env::SharedEnv;
+use crate::geom::{ChildAlign, Dir, Gen, LayoutDirs, Length, Linear, Sides, Size};
+use crate::layout::{
+ Expansion, Node, NodePad, NodePages, NodePar, NodeSpacing, NodeStack, NodeText, Tree,
+};
+
+/// The context for evaluation.
+#[derive(Debug)]
+pub struct EvalContext {
+ /// The environment from which resources are gathered.
+ pub env: SharedEnv,
+ /// The active evaluation state.
+ pub state: State,
+ /// The accumulated feedback.
+ feedback: Feedback,
+ /// The finished page runs.
+ runs: Vec<NodePages>,
+ /// The stack of logical groups (paragraphs and such).
+ ///
+ /// Each entry contains metadata about the group and nodes that are at the
+ /// same level as the group, which will return to `inner` once the group is
+ /// finished.
+ groups: Vec<(Box<dyn Any>, Vec<Node>)>,
+ /// The nodes in the current innermost group
+ /// (whose metadata is in `groups.last()`).
+ inner: Vec<Node>,
+}
+
+impl EvalContext {
+ /// Create a new evaluation context with a base state.
+ pub fn new(env: SharedEnv, state: State) -> Self {
+ Self {
+ env,
+ state,
+ groups: vec![],
+ inner: vec![],
+ runs: vec![],
+ feedback: Feedback::new(),
+ }
+ }
+
+ /// Finish evaluation and return the created document.
+ pub fn finish(self) -> Pass<Tree> {
+ assert!(self.groups.is_empty(), "unfinished group");
+ Pass::new(Tree { runs: self.runs }, self.feedback)
+ }
+
+ /// Add a diagnostic to the feedback.
+ pub fn diag(&mut self, diag: Spanned<Diag>) {
+ self.feedback.diags.push(diag);
+ }
+
+ /// Add a decoration to the feedback.
+ pub fn deco(&mut self, deco: Spanned<Deco>) {
+ self.feedback.decos.push(deco);
+ }
+
+ /// Push a layout node to the active group.
+ ///
+ /// Spacing nodes will be handled according to their [`Softness`].
+ pub fn push(&mut self, node: impl Into<Node>) {
+ let node = node.into();
+
+ if let Node::Spacing(this) = node {
+ if this.softness == Softness::Soft && self.inner.is_empty() {
+ return;
+ }
+
+ if let Some(&Node::Spacing(other)) = self.inner.last() {
+ if this.softness > other.softness {
+ self.inner.pop();
+ } else if this.softness == Softness::Soft {
+ return;
+ }
+ }
+ }
+
+ self.inner.push(node);
+ }
+
+ /// Start a page group based on the active page state.
+ ///
+ /// The `softness` is a hint on whether empty pages should be kept in the
+ /// output.
+ ///
+ /// This also starts an inner paragraph.
+ pub fn start_page_group(&mut self, softness: Softness) {
+ self.start_group(PageGroup {
+ size: self.state.page.size,
+ padding: self.state.page.margins(),
+ dirs: self.state.dirs,
+ align: self.state.align,
+ softness,
+ });
+ self.start_par_group();
+ }
+
+ /// End a page group, returning its [`Softness`].
+ ///
+ /// Whether the page is kept when it's empty is decided by `keep_empty`
+ /// based on its softness. If kept, the page is pushed to the finished page
+ /// runs.
+ ///
+ /// This also ends an inner paragraph.
+ pub fn end_page_group<F>(&mut self, keep_empty: F) -> Softness
+ where
+ F: FnOnce(Softness) -> bool,
+ {
+ self.end_par_group();
+ let (group, children) = self.end_group::<PageGroup>();
+ if !children.is_empty() || keep_empty(group.softness) {
+ self.runs.push(NodePages {
+ size: group.size,
+ child: Node::any(NodePad {
+ padding: group.padding,
+ child: Node::any(NodeStack {
+ dirs: group.dirs,
+ align: group.align,
+ expansion: Gen::uniform(Expansion::Fill),
+ children,
+ }),
+ }),
+ })
+ }
+ group.softness
+ }
+
+ /// Start a content group.
+ ///
+ /// This also starts an inner paragraph.
+ pub fn start_content_group(&mut self) {
+ self.start_group(ContentGroup);
+ self.start_par_group();
+ }
+
+ /// End a content group and return the resulting nodes.
+ ///
+ /// This also ends an inner paragraph.
+ pub fn end_content_group(&mut self) -> Vec<Node> {
+ self.end_par_group();
+ self.end_group::<ContentGroup>().1
+ }
+
+ /// Start a paragraph group based on the active text state.
+ pub fn start_par_group(&mut self) {
+ let em = self.state.font.font_size();
+ self.start_group(ParGroup {
+ dirs: self.state.dirs,
+ align: self.state.align,
+ line_spacing: self.state.par.line_spacing.resolve(em),
+ });
+ }
+
+ /// End a paragraph group and push it to its parent group if it's not empty.
+ pub fn end_par_group(&mut self) {
+ let (group, children) = self.end_group::<ParGroup>();
+ if !children.is_empty() {
+ self.push(NodePar {
+ dirs: group.dirs,
+ align: group.align,
+ // FIXME: This is a hack and should be superseded by something
+ // better.
+ cross_expansion: if self.groups.len() <= 1 {
+ Expansion::Fill
+ } else {
+ Expansion::Fit
+ },
+ line_spacing: group.line_spacing,
+ children,
+ });
+ }
+ }
+
+ /// Start a layouting group.
+ ///
+ /// All further calls to [`push`](Self::push) will collect nodes for this group.
+ /// The given metadata will be returned alongside the collected nodes
+ /// in a matching call to [`end_group`](Self::end_group).
+ fn start_group<T: 'static>(&mut self, meta: T) {
+ self.groups.push((Box::new(meta), std::mem::take(&mut self.inner)));
+ }
+
+ /// End a layouting group started with [`start_group`](Self::start_group).
+ ///
+ /// This returns the stored metadata and the collected nodes.
+ #[track_caller]
+ fn end_group<T: 'static>(&mut self) -> (T, Vec<Node>) {
+ if let Some(&Node::Spacing(spacing)) = self.inner.last() {
+ if spacing.softness == Softness::Soft {
+ self.inner.pop();
+ }
+ }
+
+ let (any, outer) = self.groups.pop().expect("no pushed group");
+ let group = *any.downcast::<T>().expect("bad group type");
+ (group, std::mem::replace(&mut self.inner, outer))
+ }
+
+ /// Set the directions if they would apply to different axes, producing an
+ /// appropriate error otherwise.
+ pub fn set_dirs(&mut self, new: Gen<Option<Spanned<Dir>>>) {
+ let dirs = Gen::new(
+ new.main.map(|s| s.v).unwrap_or(self.state.dirs.main),
+ new.cross.map(|s| s.v).unwrap_or(self.state.dirs.cross),
+ );
+
+ if dirs.main.axis() != dirs.cross.axis() {
+ self.state.dirs = dirs;
+ } else {
+ for dir in new.main.iter().chain(new.cross.iter()) {
+ self.diag(error!(dir.span, "aligned axis"));
+ }
+ }
+ }
+
+ /// Apply a forced line break.
+ pub fn apply_linebreak(&mut self) {
+ self.end_par_group();
+ self.start_par_group();
+ }
+
+ /// Apply a forced paragraph break.
+ pub fn apply_parbreak(&mut self) {
+ self.end_par_group();
+ let em = self.state.font.font_size();
+ self.push(NodeSpacing {
+ amount: self.state.par.par_spacing.resolve(em),
+ softness: Softness::Soft,
+ });
+ self.start_par_group();
+ }
+
+ /// Construct a text node from the given string based on the active text
+ /// state.
+ pub fn make_text_node(&self, text: String) -> NodeText {
+ let mut variant = self.state.font.variant;
+
+ if self.state.font.strong {
+ variant.weight = variant.weight.thicken(300);
+ }
+
+ if self.state.font.emph {
+ variant.style = match variant.style {
+ FontStyle::Normal => FontStyle::Italic,
+ FontStyle::Italic => FontStyle::Normal,
+ FontStyle::Oblique => FontStyle::Normal,
+ }
+ }
+
+ NodeText {
+ text,
+ align: self.state.align,
+ dir: self.state.dirs.cross,
+ font_size: self.state.font.font_size(),
+ families: Rc::clone(&self.state.font.families),
+ variant,
+ }
+ }
+}
+
+/// Defines how an item interacts with surrounding items.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
+pub enum Softness {
+ /// A soft item can be skipped in some circumstances.
+ Soft,
+ /// A hard item is always retained.
+ Hard,
+}
+
+/// A group for a page run.
+#[derive(Debug)]
+struct PageGroup {
+ size: Size,
+ padding: Sides<Linear>,
+ dirs: LayoutDirs,
+ align: ChildAlign,
+ softness: Softness,
+}
+
+/// A group for generic content.
+#[derive(Debug)]
+struct ContentGroup;
+
+/// A group for a paragraph.
+#[derive(Debug)]
+struct ParGroup {
+ dirs: LayoutDirs,
+ align: ChildAlign,
+ line_spacing: Length,
+}
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index 62bd444c..20d32e84 100644
--- a/src/eval/mod.rs
+++ b/src/eval/mod.rs
@@ -1,36 +1,32 @@
-//! Evaluation of syntax trees.
+//! Evaluation of syntax trees into layout trees.
#[macro_use]
mod value;
-mod args;
+mod call;
+mod context;
mod scope;
mod state;
-pub use args::*;
+pub use call::*;
+pub use context::*;
pub use scope::*;
pub use state::*;
pub use value::*;
-use std::any::Any;
use std::rc::Rc;
-use fontdock::FontStyle;
-
use crate::color::Color;
-use crate::diag::Diag;
-use crate::diag::{Deco, Feedback, Pass};
+use crate::diag::Pass;
use crate::env::SharedEnv;
-use crate::geom::{BoxAlign, Dir, Flow, Gen, Length, Linear, Relative, Sides, Size};
-use crate::layout::{
- Document, Expansion, LayoutNode, Pad, Pages, Par, Spacing, Stack, Text,
-};
+use crate::geom::{Gen, Length, Relative};
+use crate::layout::{self, Expansion, NodeSpacing, NodeStack};
use crate::syntax::*;
-/// Evaluate a syntax tree into a document.
+/// Evaluate a syntax tree into a layout tree.
///
/// The given `state` is the base state that may be updated over the course of
/// evaluation.
-pub fn eval(tree: &SynTree, env: SharedEnv, state: State) -> Pass<Document> {
+pub fn eval(tree: &Tree, env: SharedEnv, state: State) -> Pass<layout::Tree> {
let mut ctx = EvalContext::new(env, state);
ctx.start_page_group(Softness::Hard);
tree.eval(&mut ctx);
@@ -38,285 +34,6 @@ pub fn eval(tree: &SynTree, env: SharedEnv, state: State) -> Pass<Document> {
ctx.finish()
}
-/// The context for evaluation.
-#[derive(Debug)]
-pub struct EvalContext {
- /// The environment from which resources are gathered.
- pub env: SharedEnv,
- /// The active evaluation state.
- pub state: State,
- /// The accumulated feedback.
- feedback: Feedback,
- /// The finished page runs.
- runs: Vec<Pages>,
- /// The stack of logical groups (paragraphs and such).
- ///
- /// Each entry contains metadata about the group and nodes that are at the
- /// same level as the group, which will return to `inner` once the group is
- /// finished.
- groups: Vec<(Box<dyn Any>, Vec<LayoutNode>)>,
- /// The nodes in the current innermost group
- /// (whose metadata is in `groups.last()`).
- inner: Vec<LayoutNode>,
-}
-
-impl EvalContext {
- /// Create a new evaluation context with a base state.
- pub fn new(env: SharedEnv, state: State) -> Self {
- Self {
- env,
- state,
- groups: vec![],
- inner: vec![],
- runs: vec![],
- feedback: Feedback::new(),
- }
- }
-
- /// Finish evaluation and return the created document.
- pub fn finish(self) -> Pass<Document> {
- assert!(self.groups.is_empty(), "unfinished group");
- Pass::new(Document { runs: self.runs }, self.feedback)
- }
-
- /// Add a diagnostic to the feedback.
- pub fn diag(&mut self, diag: Spanned<Diag>) {
- self.feedback.diags.push(diag);
- }
-
- /// Add a decoration to the feedback.
- pub fn deco(&mut self, deco: Spanned<Deco>) {
- self.feedback.decos.push(deco);
- }
-
- /// Push a layout node to the active group.
- ///
- /// Spacing nodes will be handled according to their [`Softness`].
- pub fn push(&mut self, node: impl Into<LayoutNode>) {
- let node = node.into();
-
- if let LayoutNode::Spacing(this) = node {
- if this.softness == Softness::Soft && self.inner.is_empty() {
- return;
- }
-
- if let Some(&LayoutNode::Spacing(other)) = self.inner.last() {
- if this.softness > other.softness {
- self.inner.pop();
- } else if this.softness == Softness::Soft {
- return;
- }
- }
- }
-
- self.inner.push(node);
- }
-
- /// Start a page group based on the active page state.
- ///
- /// The `softness` is a hint on whether empty pages should be kept in the
- /// output.
- ///
- /// This also starts an inner paragraph.
- pub fn start_page_group(&mut self, softness: Softness) {
- self.start_group(PageGroup {
- size: self.state.page.size,
- padding: self.state.page.margins(),
- flow: self.state.flow,
- align: self.state.align,
- softness,
- });
- self.start_par_group();
- }
-
- /// End a page group, returning its [`Softness`].
- ///
- /// Whether the page is kept when it's empty is decided by `keep_empty`
- /// based on its softness. If kept, the page is pushed to the finished page
- /// runs.
- ///
- /// This also ends an inner paragraph.
- pub fn end_page_group(
- &mut self,
- keep_empty: impl FnOnce(Softness) -> bool,
- ) -> Softness {
- self.end_par_group();
- let (group, children) = self.end_group::<PageGroup>();
- if !children.is_empty() || keep_empty(group.softness) {
- self.runs.push(Pages {
- size: group.size,
- child: LayoutNode::dynamic(Pad {
- padding: group.padding,
- child: LayoutNode::dynamic(Stack {
- flow: group.flow,
- align: group.align,
- expansion: Gen::uniform(Expansion::Fill),
- children,
- }),
- }),
- })
- }
- group.softness
- }
-
- /// Start a content group.
- ///
- /// This also starts an inner paragraph.
- pub fn start_content_group(&mut self) {
- self.start_group(ContentGroup);
- self.start_par_group();
- }
-
- /// End a content group and return the resulting nodes.
- ///
- /// This also ends an inner paragraph.
- pub fn end_content_group(&mut self) -> Vec<LayoutNode> {
- self.end_par_group();
- self.end_group::<ContentGroup>().1
- }
-
- /// Start a paragraph group based on the active text state.
- pub fn start_par_group(&mut self) {
- let em = self.state.font.font_size();
- self.start_group(ParGroup {
- flow: self.state.flow,
- align: self.state.align,
- line_spacing: self.state.par.line_spacing.resolve(em),
- });
- }
-
- /// End a paragraph group and push it to its parent group if it's not empty.
- pub fn end_par_group(&mut self) {
- let (group, children) = self.end_group::<ParGroup>();
- if !children.is_empty() {
- // FIXME: This is a hack and should be superseded by something
- // better.
- let cross_expansion = Expansion::fill_if(self.groups.len() <= 1);
- self.push(Par {
- flow: group.flow,
- align: group.align,
- cross_expansion,
- line_spacing: group.line_spacing,
- children,
- });
- }
- }
-
- /// Start a layouting group.
- ///
- /// All further calls to [`push`](Self::push) will collect nodes for this group.
- /// The given metadata will be returned alongside the collected nodes
- /// in a matching call to [`end_group`](Self::end_group).
- fn start_group<T: 'static>(&mut self, meta: T) {
- self.groups.push((Box::new(meta), std::mem::take(&mut self.inner)));
- }
-
- /// End a layouting group started with [`start_group`](Self::start_group).
- ///
- /// This returns the stored metadata and the collected nodes.
- #[track_caller]
- fn end_group<T: 'static>(&mut self) -> (T, Vec<LayoutNode>) {
- if let Some(&LayoutNode::Spacing(spacing)) = self.inner.last() {
- if spacing.softness == Softness::Soft {
- self.inner.pop();
- }
- }
-
- let (any, outer) = self.groups.pop().expect("no pushed group");
- let group = *any.downcast::<T>().expect("bad group type");
- (group, std::mem::replace(&mut self.inner, outer))
- }
-
- /// Updates the flow directions if the resulting main and cross directions
- /// apply to different axes. Generates an appropriate error, otherwise.
- pub fn set_flow(&mut self, new: Gen<Option<Spanned<Dir>>>) {
- let flow = Gen::new(
- new.main.map(|s| s.v).unwrap_or(self.state.flow.main),
- new.cross.map(|s| s.v).unwrap_or(self.state.flow.cross),
- );
-
- if flow.main.axis() != flow.cross.axis() {
- self.state.flow = flow;
- } else {
- for dir in new.main.iter().chain(new.cross.iter()) {
- self.diag(error!(dir.span, "aligned axis"));
- }
- }
- }
-
- /// Apply a forced line break.
- pub fn apply_linebreak(&mut self) {
- self.end_par_group();
- self.start_par_group();
- }
-
- /// Apply a forced paragraph break.
- pub fn apply_parbreak(&mut self) {
- self.end_par_group();
- let em = self.state.font.font_size();
- self.push(Spacing {
- amount: self.state.par.par_spacing.resolve(em),
- softness: Softness::Soft,
- });
- self.start_par_group();
- }
-
- /// Construct a text node from the given string based on the active text
- /// state.
- pub fn make_text_node(&self, text: String) -> Text {
- let mut variant = self.state.font.variant;
-
- if self.state.font.strong {
- variant.weight = variant.weight.thicken(300);
- }
-
- if self.state.font.emph {
- variant.style = match variant.style {
- FontStyle::Normal => FontStyle::Italic,
- FontStyle::Italic => FontStyle::Normal,
- FontStyle::Oblique => FontStyle::Normal,
- }
- }
-
- Text {
- text,
- align: self.state.align,
- dir: self.state.flow.cross,
- font_size: self.state.font.font_size(),
- families: Rc::clone(&self.state.font.families),
- variant,
- }
- }
-}
-
-/// A group for page runs.
-struct PageGroup {
- size: Size,
- padding: Sides<Linear>,
- flow: Flow,
- align: BoxAlign,
- softness: Softness,
-}
-
-/// A group for generic content.
-struct ContentGroup;
-
-/// A group for paragraphs.
-struct ParGroup {
- flow: Flow,
- align: BoxAlign,
- line_spacing: Length,
-}
-
-/// Defines how an item interact with surrounding items.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
-pub enum Softness {
- /// A soft item can be skipped in some circumstances.
- Soft,
- /// A hard item is always retained.
- Hard,
-}
-
/// Evaluate an item.
///
/// _Note_: Evaluation is not necessarily pure, it may change the active state.
@@ -339,7 +56,7 @@ where
}
}
-impl Eval for &[Spanned<SynNode>] {
+impl Eval for &[Spanned<Node>] {
type Output = ();
fn eval(self, ctx: &mut EvalContext) -> Self::Output {
@@ -349,33 +66,33 @@ impl Eval for &[Spanned<SynNode>] {
}
}
-impl Eval for Spanned<&SynNode> {
+impl Eval for Spanned<&Node> {
type Output = ();
fn eval(self, ctx: &mut EvalContext) -> Self::Output {
match self.v {
- SynNode::Text(text) => {
+ Node::Text(text) => {
let node = ctx.make_text_node(text.clone());
ctx.push(node);
}
- SynNode::Space => {
+ Node::Space => {
let em = ctx.state.font.font_size();
- ctx.push(Spacing {
+ ctx.push(NodeSpacing {
amount: ctx.state.par.word_spacing.resolve(em),
softness: Softness::Soft,
});
}
- SynNode::Linebreak => ctx.apply_linebreak(),
- SynNode::Parbreak => ctx.apply_parbreak(),
+ Node::Linebreak => ctx.apply_linebreak(),
+ Node::Parbreak => ctx.apply_parbreak(),
- SynNode::Strong => ctx.state.font.strong ^= true,
- SynNode::Emph => ctx.state.font.emph ^= true,
+ Node::Strong => ctx.state.font.strong ^= true,
+ Node::Emph => ctx.state.font.emph ^= true,
- SynNode::Heading(heading) => heading.with_span(self.span).eval(ctx),
- SynNode::Raw(raw) => raw.with_span(self.span).eval(ctx),
+ Node::Heading(heading) => heading.with_span(self.span).eval(ctx),
+ Node::Raw(raw) => raw.with_span(self.span).eval(ctx),
- SynNode::Expr(expr) => {
+ Node::Expr(expr) => {
let value = expr.with_span(self.span).eval(ctx);
value.eval(ctx)
}
@@ -413,15 +130,15 @@ impl Eval for Spanned<&NodeRaw> {
let mut children = vec![];
for line in &self.v.lines {
- children.push(LayoutNode::Text(ctx.make_text_node(line.clone())));
- children.push(LayoutNode::Spacing(Spacing {
+ children.push(layout::Node::Text(ctx.make_text_node(line.clone())));
+ children.push(layout::Node::Spacing(NodeSpacing {
amount: line_spacing,
softness: Softness::Hard,
}));
}
- ctx.push(Stack {
- flow: ctx.state.flow,
+ ctx.push(NodeStack {
+ dirs: ctx.state.dirs,
align: ctx.state.align,
expansion: Gen::uniform(Expansion::Fit),
children,
@@ -436,10 +153,13 @@ impl Eval for Spanned<&Expr> {
fn eval(self, ctx: &mut EvalContext) -> Self::Output {
match self.v {
- Expr::Lit(lit) => lit.with_span(self.span).eval(ctx),
- Expr::Call(call) => call.with_span(self.span).eval(ctx),
- Expr::Unary(unary) => unary.with_span(self.span).eval(ctx),
- Expr::Binary(binary) => binary.with_span(self.span).eval(ctx),
+ Expr::Lit(v) => v.with_span(self.span).eval(ctx),
+ Expr::Call(v) => v.with_span(self.span).eval(ctx),
+ Expr::Unary(v) => v.with_span(self.span).eval(ctx),
+ Expr::Binary(v) => v.with_span(self.span).eval(ctx),
+ Expr::Array(v) => Value::Array(v.with_span(self.span).eval(ctx)),
+ Expr::Dict(v) => Value::Dict(v.with_span(self.span).eval(ctx)),
+ Expr::Content(v) => Value::Content(v.clone()),
}
}
}
@@ -463,14 +183,11 @@ impl Eval for Spanned<&Lit> {
Lit::Percent(v) => Value::Relative(Relative::new(v / 100.0)),
Lit::Color(v) => Value::Color(Color::Rgba(v)),
Lit::Str(ref v) => Value::Str(v.clone()),
- Lit::Array(ref v) => Value::Array(v.with_span(self.span).eval(ctx)),
- Lit::Dict(ref v) => Value::Dict(v.with_span(self.span).eval(ctx)),
- Lit::Content(ref v) => Value::Content(v.clone()),
}
}
}
-impl Eval for Spanned<&Array> {
+impl Eval for Spanned<&ExprArray> {
type Output = ValueArray;
fn eval(self, ctx: &mut EvalContext) -> Self::Output {
@@ -478,7 +195,7 @@ impl Eval for Spanned<&Array> {
}
}
-impl Eval for Spanned<&Dict> {
+impl Eval for Spanned<&ExprDict> {
type Output = ValueDict;
fn eval(self, ctx: &mut EvalContext) -> Self::Output {
@@ -489,36 +206,6 @@ impl Eval for Spanned<&Dict> {
}
}
-impl Eval for Spanned<&ExprCall> {
- type Output = Value;
-
- fn eval(self, ctx: &mut EvalContext) -> Self::Output {
- let name = &self.v.name.v;
- let span = self.v.name.span;
-
- if let Some(value) = ctx.state.scope.get(name) {
- if let Value::Func(func) = value {
- let func = func.clone();
- ctx.feedback.decos.push(Deco::Resolved.with_span(span));
-
- let mut args = self.v.args.as_ref().eval(ctx);
- let returned = func(ctx, &mut args);
- args.finish(ctx);
-
- return returned;
- } else {
- let ty = value.type_name();
- ctx.diag(error!(span, "a value of type {} is not callable", ty));
- }
- } else if !name.is_empty() {
- ctx.diag(error!(span, "unknown function"));
- }
-
- ctx.feedback.decos.push(Deco::Unresolved.with_span(span));
- Value::Error
- }
-}
-
impl Eval for Spanned<&ExprUnary> {
type Output = Value;
diff --git a/src/eval/scope.rs b/src/eval/scope.rs
index c9ce1423..dd7cc1da 100644
--- a/src/eval/scope.rs
+++ b/src/eval/scope.rs
@@ -1,11 +1,9 @@
-//! Mapping from identifiers to functions.
-
use std::collections::HashMap;
use std::fmt::{self, Debug, Formatter};
use super::Value;
-/// A map from identifiers to functions.
+/// A map from identifiers to values.
#[derive(Default, Clone, PartialEq)]
pub struct Scope {
values: HashMap<String, Value>,
diff --git a/src/eval/state.rs b/src/eval/state.rs
index 3f3ac8e4..9cdafaf2 100644
--- a/src/eval/state.rs
+++ b/src/eval/state.rs
@@ -1,46 +1,46 @@
-//! Evaluation state.
-
use std::rc::Rc;
use fontdock::{fallback, FallbackTree, FontStretch, FontStyle, FontVariant, FontWeight};
use super::Scope;
-use crate::geom::{Align, BoxAlign, Dir, Flow, Length, Linear, Relative, Sides, Size};
+use crate::geom::{
+ Align, ChildAlign, Dir, LayoutDirs, Length, Linear, Relative, Sides, Size,
+};
use crate::paper::{Paper, PaperClass, PAPER_A4};
-/// The active evaluation state.
+/// The evaluation state.
#[derive(Debug, Clone, PartialEq)]
pub struct State {
- /// The scope that contains function definitions.
+ /// The scope that contains variable definitions.
pub scope: Scope,
- /// The page state.
- pub page: PageState,
- /// The paragraph state.
- pub par: ParState,
- /// The font state.
- pub font: FontState,
- /// The active layouting directions.
- pub flow: Flow,
- /// The active box alignments.
- pub align: BoxAlign,
+ /// The current page state.
+ pub page: StatePage,
+ /// The current paragraph state.
+ pub par: StatePar,
+ /// The current font state.
+ pub font: StateFont,
+ /// The current directions.
+ pub dirs: LayoutDirs,
+ /// The current alignments.
+ pub align: ChildAlign,
}
impl Default for State {
fn default() -> Self {
Self {
scope: crate::library::_std(),
- page: PageState::default(),
- par: ParState::default(),
- font: FontState::default(),
- flow: Flow::new(Dir::TTB, Dir::LTR),
- align: BoxAlign::new(Align::Start, Align::Start),
+ page: StatePage::default(),
+ par: StatePar::default(),
+ font: StateFont::default(),
+ dirs: LayoutDirs::new(Dir::TTB, Dir::LTR),
+ align: ChildAlign::new(Align::Start, Align::Start),
}
}
}
/// Defines page properties.
#[derive(Debug, Copy, Clone, PartialEq)]
-pub struct PageState {
+pub struct StatePage {
/// The class of this page.
pub class: PaperClass,
/// The width and height of the page.
@@ -50,7 +50,7 @@ pub struct PageState {
pub margins: Sides<Option<Linear>>,
}
-impl PageState {
+impl StatePage {
/// The default page style for the given paper.
pub fn new(paper: Paper) -> Self {
Self {
@@ -72,7 +72,7 @@ impl PageState {
}
}
-impl Default for PageState {
+impl Default for StatePage {
fn default() -> Self {
Self::new(PAPER_A4)
}
@@ -80,7 +80,7 @@ impl Default for PageState {
/// Defines paragraph properties.
#[derive(Debug, Copy, Clone, PartialEq)]
-pub struct ParState {
+pub struct StatePar {
/// The spacing between words (dependent on scaled font size).
pub word_spacing: Linear,
/// The spacing between lines (dependent on scaled font size).
@@ -89,7 +89,7 @@ pub struct ParState {
pub par_spacing: Linear,
}
-impl Default for ParState {
+impl Default for StatePar {
fn default() -> Self {
Self {
word_spacing: Relative::new(0.25).into(),
@@ -101,7 +101,7 @@ impl Default for ParState {
/// Defines font properties.
#[derive(Debug, Clone, PartialEq)]
-pub struct FontState {
+pub struct StateFont {
/// A tree of font family names and generic class names.
pub families: Rc<FallbackTree>,
/// The selected font variant.
@@ -118,14 +118,14 @@ pub struct FontState {
pub emph: bool,
}
-impl FontState {
+impl StateFont {
/// The absolute font size.
pub fn font_size(&self) -> Length {
self.scale.resolve(self.size)
}
}
-impl Default for FontState {
+impl Default for StateFont {
fn default() -> Self {
Self {
families: Rc::new(default_font_families()),
@@ -150,8 +150,6 @@ fn default_font_families() -> FallbackTree {
"serif" => ["source serif pro", "noto serif"],
"sans-serif" => ["source sans pro", "noto sans"],
"monospace" => ["source code pro", "noto sans mono"],
- "emoji" => ["segoe ui emoji", "noto emoji"],
- "math" => ["latin modern math", "serif"],
},
base: [
"source sans pro",
diff --git a/src/eval/value.rs b/src/eval/value.rs
index d1dcdcfa..a91ff137 100644
--- a/src/eval/value.rs
+++ b/src/eval/value.rs
@@ -1,5 +1,3 @@
-//! Computational values.
-
use std::any::Any;
use std::collections::HashMap;
use std::fmt::{self, Debug, Formatter};
@@ -9,7 +7,7 @@ use std::rc::Rc;
use super::{Args, Eval, EvalContext};
use crate::color::Color;
use crate::geom::{Length, Linear, Relative};
-use crate::syntax::{Spanned, SynTree, WithSpan};
+use crate::syntax::{Spanned, Tree, WithSpan};
/// A computational value.
#[derive(Clone, PartialEq)]
@@ -47,6 +45,14 @@ pub enum Value {
}
impl Value {
+ /// Create a new dynamic value.
+ pub fn any<T>(any: T) -> Self
+ where
+ T: Type + Debug + Clone + PartialEq + 'static,
+ {
+ Self::Any(ValueAny::new(any))
+ }
+
/// Try to cast the value into a specific type.
pub fn cast<T>(self) -> CastResult<T, Self>
where
@@ -130,7 +136,7 @@ pub type ValueArray = Vec<Value>;
pub type ValueDict = HashMap<String, Value>;
/// A content value: `{*Hi* there}`.
-pub type ValueContent = SynTree;
+pub type ValueContent = Tree;
/// A wrapper around a reference-counted executable function.
#[derive(Clone)]
@@ -197,7 +203,7 @@ impl ValueAny {
self.0.as_any().downcast_ref()
}
- /// The name of the stored object's type.
+ /// The name of the stored value's type.
pub fn type_name(&self) -> &'static str {
self.0.dyn_type_name()
}
@@ -289,7 +295,7 @@ pub enum CastResult<T, V> {
}
impl<T, V> CastResult<T, V> {
- /// Access the conversion resulting, discarding a possibly existing warning.
+ /// Access the conversion result, discarding a possibly existing warning.
pub fn ok(self) -> Option<T> {
match self {
CastResult::Ok(t) | CastResult::Warn(t, _) => Some(t),
@@ -399,7 +405,7 @@ impl From<ValueAny> for Value {
}
}
-/// Make a type usable with [`ValueAny`].
+/// Make a type usable as a [`Value`].
///
/// Given a type `T`, this implements the following traits:
/// - [`Type`] for `T`,
@@ -419,7 +425,7 @@ macro_rules! impl_type {
impl From<$type> for $crate::eval::Value {
fn from(any: $type) -> Self {
- $crate::eval::Value::Any($crate::eval::ValueAny::new(any))
+ $crate::eval::Value::any(any)
}
}
diff --git a/src/export/pdf.rs b/src/export/pdf.rs
index 5a2aa0cc..03c9ae95 100644
--- a/src/export/pdf.rs
+++ b/src/export/pdf.rs
@@ -15,22 +15,22 @@ use ttf_parser::{name_id, GlyphId};
use crate::env::{Env, ImageResource, ResourceId};
use crate::geom::Length;
-use crate::layout::{BoxLayout, LayoutElement};
+use crate::layout::{Element, Frame};
-/// Export a list of layouts into a _PDF_ document.
+/// Export a collection of frames into a _PDF_ document.
///
-/// This creates one page per layout. Additionally to the layouts, you need to
-/// pass in the font loader used for typesetting such that the fonts can be
-/// included in the _PDF_.
+/// This creates one page per frame. In addition to the frames, you need to pass
+/// in the environment used for typesetting such that things like fonts and
+/// images can be included in the _PDF_.
///
/// Returns the raw bytes making up the _PDF_ document.
-pub fn export(layouts: &[BoxLayout], env: &Env) -> Vec<u8> {
- PdfExporter::new(layouts, env).write()
+pub fn export(frames: &[Frame], env: &Env) -> Vec<u8> {
+ PdfExporter::new(frames, env).write()
}
struct PdfExporter<'a> {
writer: PdfWriter,
- layouts: &'a [BoxLayout],
+ frames: &'a [Frame],
env: &'a Env,
refs: Refs,
fonts: Remapper<FaceId>,
@@ -38,7 +38,7 @@ struct PdfExporter<'a> {
}
impl<'a> PdfExporter<'a> {
- fn new(layouts: &'a [BoxLayout], env: &'a Env) -> Self {
+ fn new(frames: &'a [Frame], env: &'a Env) -> Self {
let mut writer = PdfWriter::new(1, 7);
writer.set_indent(2);
@@ -46,11 +46,11 @@ impl<'a> PdfExporter<'a> {
let mut images = Remapper::new();
let mut alpha_masks = 0;
- for layout in layouts {
- for (_, element) in &layout.elements {
+ for frame in frames {
+ for (_, element) in &frame.elements {
match element {
- LayoutElement::Text(shaped) => fonts.insert(shaped.face),
- LayoutElement::Image(image) => {
+ Element::Text(shaped) => fonts.insert(shaped.face),
+ Element::Image(image) => {
let img = env.resources.loaded::<ImageResource>(image.res);
if img.buf.color().has_alpha() {
alpha_masks += 1;
@@ -61,16 +61,9 @@ impl<'a> PdfExporter<'a> {
}
}
- let refs = Refs::new(layouts.len(), fonts.len(), images.len(), alpha_masks);
+ let refs = Refs::new(frames.len(), fonts.len(), images.len(), alpha_masks);
- Self {
- writer,
- layouts,
- env,
- refs,
- fonts,
- images,
- }
+ Self { writer, frames, env, refs, fonts, images }
}
fn write(mut self) -> Vec<u8> {
@@ -110,7 +103,7 @@ impl<'a> PdfExporter<'a> {
// The page objects (non-root nodes in the page tree).
for ((page_id, content_id), page) in
- self.refs.pages().zip(self.refs.contents()).zip(self.layouts)
+ self.refs.pages().zip(self.refs.contents()).zip(self.frames)
{
self.writer
.page(page_id)
@@ -126,12 +119,12 @@ impl<'a> PdfExporter<'a> {
}
fn write_pages(&mut self) {
- for (id, page) in self.refs.contents().zip(self.layouts) {
+ for (id, page) in self.refs.contents().zip(self.frames) {
self.write_page(id, &page);
}
}
- fn write_page(&mut self, id: Ref, page: &'a BoxLayout) {
+ fn write_page(&mut self, id: Ref, page: &'a Frame) {
let mut content = Content::new();
// We only write font switching actions when the used face changes. To
@@ -141,7 +134,7 @@ impl<'a> PdfExporter<'a> {
let mut text = content.text();
for (pos, element) in &page.elements {
- if let LayoutElement::Text(shaped) = element {
+ if let Element::Text(shaped) = element {
// Check if we need to issue a font switching action.
if shaped.face != face || shaped.font_size != size {
face = shaped.face;
@@ -161,7 +154,7 @@ impl<'a> PdfExporter<'a> {
drop(text);
for (pos, element) in &page.elements {
- if let LayoutElement::Image(image) = element {
+ if let Element::Image(image) = element {
let name = format!("Im{}", self.images.map(image.res));
let size = image.size;
let x = pos.x.to_pt() as f32;
@@ -359,12 +352,12 @@ struct FontRefs {
impl Refs {
const OBJECTS_PER_FONT: usize = 5;
- fn new(layouts: usize, fonts: usize, images: usize, alpha_masks: usize) -> Self {
+ fn new(frames: usize, fonts: usize, images: usize, alpha_masks: usize) -> Self {
let catalog = 1;
let page_tree = catalog + 1;
let pages_start = page_tree + 1;
- let contents_start = pages_start + layouts as i32;
- let fonts_start = contents_start + layouts as i32;
+ let contents_start = pages_start + frames as i32;
+ let fonts_start = contents_start + frames as i32;
let images_start = fonts_start + (Self::OBJECTS_PER_FONT * fonts) as i32;
let alpha_masks_start = images_start + images as i32;
let end = alpha_masks_start + alpha_masks as i32;
diff --git a/src/font.rs b/src/font.rs
index 68a2db67..40ec5918 100644
--- a/src/font.rs
+++ b/src/font.rs
@@ -3,19 +3,16 @@
use fontdock::{ContainsChar, FaceFromVec, FontSource};
use ttf_parser::Face;
-/// A font loader backed by a dynamic source.
-pub type FontLoader = fontdock::FontLoader<Box<DynSource>>;
-
-/// The dynamic font source.
-pub type DynSource = dyn FontSource<Face = OwnedFace>;
+/// A font loader that is backed by a dynamic source.
+pub type FontLoader = fontdock::FontLoader<Box<dyn FontSource<Face = FaceBuf>>>;
/// An owned font face.
-pub struct OwnedFace {
+pub struct FaceBuf {
data: Box<[u8]>,
face: Face<'static>,
}
-impl OwnedFace {
+impl FaceBuf {
/// Get a reference to the underlying face.
pub fn get(&self) -> &Face<'_> {
// We can't implement Deref because that would leak the internal 'static
@@ -29,7 +26,7 @@ impl OwnedFace {
}
}
-impl FaceFromVec for OwnedFace {
+impl FaceFromVec for FaceBuf {
fn from_vec(vec: Vec<u8>, i: u32) -> Option<Self> {
let data = vec.into_boxed_slice();
@@ -45,7 +42,7 @@ impl FaceFromVec for OwnedFace {
}
}
-impl ContainsChar for OwnedFace {
+impl ContainsChar for FaceBuf {
fn contains_char(&self, c: char) -> bool {
self.get().glyph_index(c).is_some()
}
diff --git a/src/geom/align.rs b/src/geom/align.rs
index 7c4d965f..8f02a4ea 100644
--- a/src/geom/align.rs
+++ b/src/geom/align.rs
@@ -1,7 +1,7 @@
use super::*;
-/// The alignment of a box in a container.
-pub type BoxAlign = Gen<Align>;
+/// The alignment of a child in a container.
+pub type ChildAlign = Gen<Align>;
/// Where to align something along a directed axis.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
diff --git a/src/geom/dir.rs b/src/geom/dir.rs
index f7ffa3e2..c5eaa3a6 100644
--- a/src/geom/dir.rs
+++ b/src/geom/dir.rs
@@ -1,7 +1,7 @@
use super::*;
-/// The directions along which content flows in a container.
-pub type Flow = Gen<Dir>;
+/// The directions along which nodes are layouted.
+pub type LayoutDirs = Gen<Dir>;
/// The four directions into which content can be laid out.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
diff --git a/src/geom/gen.rs b/src/geom/gen.rs
index 11b117ea..8723ea99 100644
--- a/src/geom/gen.rs
+++ b/src/geom/gen.rs
@@ -50,8 +50,8 @@ impl<T> Get<GenAxis> for Gen<T> {
impl<T> Switch for Gen<T> {
type Other = Spec<T>;
- fn switch(self, flow: Flow) -> Self::Other {
- match flow.main.axis() {
+ fn switch(self, dirs: LayoutDirs) -> Self::Other {
+ match dirs.main.axis() {
SpecAxis::Horizontal => Spec::new(self.main, self.cross),
SpecAxis::Vertical => Spec::new(self.cross, self.main),
}
@@ -80,10 +80,10 @@ impl GenAxis {
impl Switch for GenAxis {
type Other = SpecAxis;
- fn switch(self, flow: Flow) -> Self::Other {
+ fn switch(self, dirs: LayoutDirs) -> Self::Other {
match self {
- Self::Main => flow.main.axis(),
- Self::Cross => flow.cross.axis(),
+ Self::Main => dirs.main.axis(),
+ Self::Cross => dirs.cross.axis(),
}
}
}
diff --git a/src/geom/mod.rs b/src/geom/mod.rs
index 0589346e..69bb0898 100644
--- a/src/geom/mod.rs
+++ b/src/geom/mod.rs
@@ -50,5 +50,5 @@ pub trait Switch {
/// The other version of this type based on the current layouting
/// directions.
- fn switch(self, flow: Flow) -> Self::Other;
+ fn switch(self, dirs: LayoutDirs) -> Self::Other;
}
diff --git a/src/geom/point.rs b/src/geom/point.rs
index 4523a861..0faa781c 100644
--- a/src/geom/point.rs
+++ b/src/geom/point.rs
@@ -45,8 +45,8 @@ impl Get<SpecAxis> for Point {
impl Switch for Point {
type Other = Gen<Length>;
- fn switch(self, flow: Flow) -> Self::Other {
- match flow.main.axis() {
+ fn switch(self, dirs: LayoutDirs) -> Self::Other {
+ match dirs.main.axis() {
SpecAxis::Horizontal => Gen::new(self.x, self.y),
SpecAxis::Vertical => Gen::new(self.y, self.x),
}
diff --git a/src/geom/size.rs b/src/geom/size.rs
index 28984659..bc233e5c 100644
--- a/src/geom/size.rs
+++ b/src/geom/size.rs
@@ -53,8 +53,8 @@ impl Get<SpecAxis> for Size {
impl Switch for Size {
type Other = Gen<Length>;
- fn switch(self, flow: Flow) -> Self::Other {
- match flow.main.axis() {
+ fn switch(self, dirs: LayoutDirs) -> Self::Other {
+ match dirs.main.axis() {
SpecAxis::Horizontal => Gen::new(self.width, self.height),
SpecAxis::Vertical => Gen::new(self.height, self.width),
}
diff --git a/src/geom/spec.rs b/src/geom/spec.rs
index f259ce25..c61f9526 100644
--- a/src/geom/spec.rs
+++ b/src/geom/spec.rs
@@ -66,8 +66,8 @@ impl<T> Get<SpecAxis> for Spec<T> {
impl<T> Switch for Spec<T> {
type Other = Gen<T>;
- fn switch(self, flow: Flow) -> Self::Other {
- match flow.main.axis() {
+ fn switch(self, dirs: LayoutDirs) -> Self::Other {
+ match dirs.main.axis() {
SpecAxis::Horizontal => Gen::new(self.horizontal, self.vertical),
SpecAxis::Vertical => Gen::new(self.vertical, self.horizontal),
}
@@ -96,11 +96,11 @@ impl SpecAxis {
impl Switch for SpecAxis {
type Other = GenAxis;
- fn switch(self, flow: Flow) -> Self::Other {
- if self == flow.main.axis() {
+ fn switch(self, dirs: LayoutDirs) -> Self::Other {
+ if self == dirs.main.axis() {
GenAxis::Main
} else {
- debug_assert_eq!(self, flow.cross.axis());
+ debug_assert_eq!(self, dirs.cross.axis());
GenAxis::Cross
}
}
diff --git a/src/layout/fixed.rs b/src/layout/fixed.rs
index d0daa2ca..2ec46df3 100644
--- a/src/layout/fixed.rs
+++ b/src/layout/fixed.rs
@@ -3,16 +3,16 @@ use crate::geom::Linear;
/// A node that can fix its child's width and height.
#[derive(Debug, Clone, PartialEq)]
-pub struct Fixed {
+pub struct NodeFixed {
/// The fixed width, if any.
pub width: Option<Linear>,
/// The fixed height, if any.
pub height: Option<Linear>,
/// The child node whose size to fix.
- pub child: LayoutNode,
+ pub child: Node,
}
-impl Layout for Fixed {
+impl Layout for NodeFixed {
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted {
let Area { rem, full } = areas.current;
let size = Size::new(
@@ -25,8 +25,8 @@ impl Layout for Fixed {
}
}
-impl From<Fixed> for LayoutNode {
- fn from(fixed: Fixed) -> Self {
- Self::dynamic(fixed)
+impl From<NodeFixed> for Node {
+ fn from(fixed: NodeFixed) -> Self {
+ Self::any(fixed)
}
}
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index 44a2c2fa..d09566e3 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -1,4 +1,4 @@
-//! Layouting of documents.
+//! Layouting.
mod fixed;
mod node;
@@ -20,10 +20,48 @@ pub use spacing::*;
pub use stack::*;
pub use text::*;
-/// Layout a document and return the produced layouts.
-pub fn layout(document: &Document, env: SharedEnv) -> Vec<BoxLayout> {
- let mut ctx = LayoutContext { env };
- document.layout(&mut ctx)
+/// Layout a tree into a collection of frames.
+pub fn layout(tree: &Tree, env: SharedEnv) -> Vec<Frame> {
+ tree.layout(&mut LayoutContext { env })
+}
+
+/// A tree of layout nodes.
+#[derive(Debug, Clone, PartialEq)]
+pub struct Tree {
+ /// Runs of pages with the same properties.
+ pub runs: Vec<NodePages>,
+}
+
+impl Tree {
+ /// Layout the tree into a collection of frames.
+ pub fn layout(&self, ctx: &mut LayoutContext) -> Vec<Frame> {
+ self.runs.iter().flat_map(|run| run.layout(ctx)).collect()
+ }
+}
+
+/// A run of pages that all have the same properties.
+#[derive(Debug, Clone, PartialEq)]
+pub struct NodePages {
+ /// The size of each page.
+ pub size: Size,
+ /// The layout node that produces the actual pages (typically a
+ /// [`NodeStack`]).
+ pub child: Node,
+}
+
+impl NodePages {
+ /// Layout the page run.
+ pub fn layout(&self, ctx: &mut LayoutContext) -> Vec<Frame> {
+ let areas = Areas::repeat(self.size);
+ let layouted = self.child.layout(ctx, &areas);
+ layouted.frames()
+ }
+}
+
+/// Layout a node.
+pub trait Layout {
+ /// Layout the node into the given areas.
+ fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted;
}
/// The context for layouting.
@@ -33,22 +71,30 @@ pub struct LayoutContext {
pub env: SharedEnv,
}
-/// Layout a node.
-pub trait Layout {
- /// Layout the node into the given areas.
- fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted;
+/// An area into which content can be laid out.
+#[derive(Debug, Copy, Clone, PartialEq)]
+pub struct Area {
+ /// The remaining size of this area.
+ pub rem: Size,
+ /// The full size this area once had (used for relative sizing).
+ pub full: Size,
}
-/// A sequence of areas to layout into.
+impl Area {
+ /// Create a new area.
+ pub fn new(size: Size) -> Self {
+ Self { rem: size, full: size }
+ }
+}
+
+/// A collection of areas to layout into.
#[derive(Debug, Clone, PartialEq)]
pub struct Areas {
/// The current area.
pub current: Area,
- /// The backlog of followup areas.
- ///
- /// _Note_: This works stack-like and not queue-like!
+ /// A stack of followup areas (the next area is the last element).
pub backlog: Vec<Size>,
- /// The last area that is repeated when the backlog is empty.
+ /// The final area that is repeated when the backlog is empty.
pub last: Option<Size>,
}
@@ -86,136 +132,81 @@ impl Areas {
}
}
-/// The area into which content can be laid out.
-#[derive(Debug, Copy, Clone, PartialEq)]
-pub struct Area {
- /// The remaining size of this area.
- pub rem: Size,
- /// The full size this area once had (used for relative sizing).
- pub full: Size,
-}
-
-impl Area {
- /// Create a new area.
- pub fn new(size: Size) -> Self {
- Self { rem: size, full: size }
- }
-}
-
-/// How to determine a container's size along an axis.
-#[derive(Debug, Copy, Clone, Eq, PartialEq)]
-pub enum Expansion {
- /// Fit the content.
- Fit,
- /// Fill the available space.
- Fill,
-}
-
-impl Expansion {
- /// Returns `Fill` if the condition is true and `Fit` otherwise.
- pub fn fill_if(condition: bool) -> Self {
- if condition { Self::Fill } else { Self::Fit }
- }
-}
-
-/// The result of [layouting](Layout::layout) a node.
+/// The result of layouting a node.
#[derive(Debug, Clone, PartialEq)]
pub enum Layouted {
/// Spacing that should be added to the parent.
Spacing(Length),
/// A layout that should be added to and aligned in the parent.
- Layout(BoxLayout, BoxAlign),
+ Frame(Frame, ChildAlign),
/// Multiple layouts.
- Layouts(Vec<BoxLayout>, BoxAlign),
+ Frames(Vec<Frame>, ChildAlign),
}
impl Layouted {
- /// Return all layouts contained in this variant (zero, one or arbitrarily
+ /// Return all frames contained in this variant (zero, one or arbitrarily
/// many).
- pub fn into_layouts(self) -> Vec<BoxLayout> {
+ pub fn frames(self) -> Vec<Frame> {
match self {
Self::Spacing(_) => vec![],
- Self::Layout(layout, _) => vec![layout],
- Self::Layouts(layouts, _) => layouts,
+ Self::Frame(frame, _) => vec![frame],
+ Self::Frames(frames, _) => frames,
}
}
}
-/// A finished box with content at fixed positions.
+/// Whether to expand or shrink a node along an axis.
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum Expansion {
+ /// Fit the content.
+ Fit,
+ /// Fill the available space.
+ Fill,
+}
+
+/// A finished layout with elements at fixed positions.
#[derive(Debug, Clone, PartialEq)]
-pub struct BoxLayout {
- /// The size of the box.
+pub struct Frame {
+ /// The size of the frame.
pub size: Size,
/// The elements composing this layout.
- pub elements: Vec<(Point, LayoutElement)>,
+ pub elements: Vec<(Point, Element)>,
}
-impl BoxLayout {
- /// Create a new empty collection.
+impl Frame {
+ /// Create a new, empty frame.
pub fn new(size: Size) -> Self {
Self { size, elements: vec![] }
}
/// Add an element at a position.
- pub fn push(&mut self, pos: Point, element: LayoutElement) {
+ pub fn push(&mut self, pos: Point, element: Element) {
self.elements.push((pos, element));
}
- /// Add all elements of another collection, placing them relative to the
- /// given position.
- pub fn push_layout(&mut self, pos: Point, more: Self) {
- for (subpos, element) in more.elements {
+ /// Add all elements of another frame, placing them relative to the given
+ /// position.
+ pub fn push_frame(&mut self, pos: Point, subframe: Self) {
+ for (subpos, element) in subframe.elements {
self.push(pos + subpos, element);
}
}
}
-/// A layout element, the basic building block layouts are composed of.
+/// The building block frames are composed of.
#[derive(Debug, Clone, PartialEq)]
-pub enum LayoutElement {
+pub enum Element {
/// Shaped text.
Text(Shaped),
/// An image.
- Image(ImageElement),
+ Image(Image),
}
-/// An image.
+/// An image element.
#[derive(Debug, Clone, PartialEq)]
-pub struct ImageElement {
- /// The image.
+pub struct Image {
+ /// The image resource.
pub res: ResourceId,
- /// The document size of the image.
+ /// The size of the image in the document.
pub size: Size,
}
-
-/// The top-level layout node.
-#[derive(Debug, Clone, PartialEq)]
-pub struct Document {
- /// The runs of pages with same properties.
- pub runs: Vec<Pages>,
-}
-
-impl Document {
- /// Layout the document.
- pub fn layout(&self, ctx: &mut LayoutContext) -> Vec<BoxLayout> {
- self.runs.iter().flat_map(|run| run.layout(ctx)).collect()
- }
-}
-
-/// A variable-length run of pages that all have the same properties.
-#[derive(Debug, Clone, PartialEq)]
-pub struct Pages {
- /// The size of each page.
- pub size: Size,
- /// The layout node that produces the actual pages (typically a [`Stack`]).
- pub child: LayoutNode,
-}
-
-impl Pages {
- /// Layout the page run.
- pub fn layout(&self, ctx: &mut LayoutContext) -> Vec<BoxLayout> {
- let areas = Areas::repeat(self.size);
- let layouted = self.child.layout(ctx, &areas);
- layouted.into_layouts()
- }
-}
diff --git a/src/layout/node.rs b/src/layout/node.rs
index 44767898..c945ee19 100644
--- a/src/layout/node.rs
+++ b/src/layout/node.rs
@@ -1,91 +1,89 @@
-//! Layout nodes.
-
use std::any::Any;
use std::fmt::{self, Debug, Formatter};
use super::*;
-/// A self-contained, styled layout node.
+/// A self-contained layout node.
#[derive(Clone, PartialEq)]
-pub enum LayoutNode {
- /// A spacing node.
- Spacing(Spacing),
+pub enum Node {
/// A text node.
- Text(Text),
- /// A dynamic that can implement custom layouting behaviour.
- Dyn(Dynamic),
+ Text(NodeText),
+ /// A spacing node.
+ Spacing(NodeSpacing),
+ /// A dynamic node that can implement custom layouting behaviour.
+ Any(NodeAny),
}
-impl LayoutNode {
+impl Node {
/// Create a new dynamic node.
- pub fn dynamic<T>(inner: T) -> Self
+ pub fn any<T>(any: T) -> Self
where
T: Layout + Debug + Clone + PartialEq + 'static,
{
- Self::Dyn(Dynamic::new(inner))
+ Self::Any(NodeAny::new(any))
}
}
-impl Layout for LayoutNode {
+impl Layout for Node {
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted {
match self {
Self::Spacing(spacing) => spacing.layout(ctx, areas),
Self::Text(text) => text.layout(ctx, areas),
- Self::Dyn(dynamic) => dynamic.layout(ctx, areas),
+ Self::Any(any) => any.layout(ctx, areas),
}
}
}
-impl Debug for LayoutNode {
+impl Debug for Node {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::Spacing(spacing) => spacing.fmt(f),
Self::Text(text) => text.fmt(f),
- Self::Dyn(dynamic) => dynamic.fmt(f),
+ Self::Any(any) => any.fmt(f),
}
}
}
/// A wrapper around a dynamic layouting node.
-pub struct Dynamic(Box<dyn Bounds>);
+pub struct NodeAny(Box<dyn Bounds>);
-impl Dynamic {
+impl NodeAny {
/// Create a new instance from any node that satisifies the required bounds.
- pub fn new<T>(inner: T) -> Self
+ pub fn new<T>(any: T) -> Self
where
T: Layout + Debug + Clone + PartialEq + 'static,
{
- Self(Box::new(inner))
+ Self(Box::new(any))
}
}
-impl Layout for Dynamic {
+impl Layout for NodeAny {
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted {
self.0.layout(ctx, areas)
}
}
-impl Clone for Dynamic {
+impl Clone for NodeAny {
fn clone(&self) -> Self {
Self(self.0.dyn_clone())
}
}
-impl PartialEq for Dynamic {
+impl PartialEq for NodeAny {
fn eq(&self, other: &Self) -> bool {
self.0.dyn_eq(other.0.as_ref())
}
}
-impl Debug for Dynamic {
+impl Debug for NodeAny {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
-impl From<Dynamic> for LayoutNode {
- fn from(dynamic: Dynamic) -> Self {
- Self::Dyn(dynamic)
+impl From<NodeAny> for Node {
+ fn from(dynamic: NodeAny) -> Self {
+ Self::Any(dynamic)
}
}
diff --git a/src/layout/pad.rs b/src/layout/pad.rs
index 00830a07..f947a7f5 100644
--- a/src/layout/pad.rs
+++ b/src/layout/pad.rs
@@ -1,26 +1,26 @@
use super::*;
use crate::geom::Linear;
-/// A node that pads its child at the sides.
+/// A node that adds padding to its child.
#[derive(Debug, Clone, PartialEq)]
-pub struct Pad {
+pub struct NodePad {
/// The amount of padding.
pub padding: Sides<Linear>,
/// The child node whose sides to pad.
- pub child: LayoutNode,
+ pub child: Node,
}
-impl Layout for Pad {
+impl Layout for NodePad {
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted {
- let areas = shrink_areas(areas, self.padding);
+ let areas = shrink(areas, self.padding);
let mut layouted = self.child.layout(ctx, &areas);
match &mut layouted {
Layouted::Spacing(_) => {}
- Layouted::Layout(layout, _) => pad_layout(layout, self.padding),
- Layouted::Layouts(layouts, _) => {
- for layout in layouts {
- pad_layout(layout, self.padding);
+ Layouted::Frame(frame, _) => pad(frame, self.padding),
+ Layouted::Frames(frames, _) => {
+ for frame in frames {
+ pad(frame, self.padding);
}
}
}
@@ -29,14 +29,14 @@ impl Layout for Pad {
}
}
-impl From<Pad> for LayoutNode {
- fn from(pad: Pad) -> Self {
- Self::dynamic(pad)
+impl From<NodePad> for Node {
+ fn from(pad: NodePad) -> Self {
+ Self::any(pad)
}
}
/// Shrink all areas by the padding.
-fn shrink_areas(areas: &Areas, padding: Sides<Linear>) -> Areas {
+fn shrink(areas: &Areas, padding: Sides<Linear>) -> Areas {
let shrink = |size| size - padding.resolve(size).size();
Areas {
current: Area {
@@ -49,12 +49,12 @@ fn shrink_areas(areas: &Areas, padding: Sides<Linear>) -> Areas {
}
/// Enlarge the box and move all elements inwards.
-fn pad_layout(layout: &mut BoxLayout, padding: Sides<Linear>) {
- let padding = padding.resolve(layout.size);
+fn pad(frame: &mut Frame, padding: Sides<Linear>) {
+ let padding = padding.resolve(frame.size);
let origin = Point::new(padding.left, padding.top);
- layout.size += padding.size();
- for (point, _) in &mut layout.elements {
+ frame.size += padding.size();
+ for (point, _) in &mut frame.elements {
*point += origin;
}
}
diff --git a/src/layout/par.rs b/src/layout/par.rs
index 723f27cb..3a8e3969 100644
--- a/src/layout/par.rs
+++ b/src/layout/par.rs
@@ -2,69 +2,67 @@ use super::*;
/// A node that arranges its children into a paragraph.
#[derive(Debug, Clone, PartialEq)]
-pub struct Par {
+pub struct NodePar {
/// The `main` and `cross` directions of this paragraph.
///
/// The children are placed in lines along the `cross` direction. The lines
/// are stacked along the `main` direction.
- pub flow: Flow,
+ pub dirs: LayoutDirs,
/// Whether to expand the cross axis to fill the area or to fit the content.
pub cross_expansion: Expansion,
/// The spacing to insert after each line.
pub line_spacing: Length,
/// The nodes to be arranged in a paragraph.
- pub children: Vec<LayoutNode>,
+ pub children: Vec<Node>,
/// How to align this paragraph in _its_ parent.
- pub align: BoxAlign,
+ pub align: ChildAlign,
}
-impl Layout for Par {
+impl Layout for NodePar {
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted {
let mut layouter = ParLayouter::new(self, areas.clone());
for child in &self.children {
match child.layout(ctx, &layouter.areas) {
Layouted::Spacing(spacing) => layouter.push_spacing(spacing),
- Layouted::Layout(layout, align) => {
- layouter.push_layout(layout, align.cross)
- }
- Layouted::Layouts(layouts, align) => {
- for layout in layouts {
- layouter.push_layout(layout, align.cross);
+ Layouted::Frame(frame, align) => layouter.push_frame(frame, align.cross),
+ Layouted::Frames(frames, align) => {
+ for frame in frames {
+ layouter.push_frame(frame, align.cross);
}
}
}
}
- Layouted::Layouts(layouter.finish(), self.align)
+ Layouted::Frames(layouter.finish(), self.align)
}
}
-impl From<Par> for LayoutNode {
- fn from(par: Par) -> Self {
- Self::dynamic(par)
+impl From<NodePar> for Node {
+ fn from(par: NodePar) -> Self {
+ Self::any(par)
}
}
struct ParLayouter<'a> {
- par: &'a Par,
+ par: &'a NodePar,
main: SpecAxis,
cross: SpecAxis,
- flow: Flow,
+ dirs: LayoutDirs,
areas: Areas,
- finished: Vec<BoxLayout>,
- lines: Vec<(Length, BoxLayout, Align)>,
+ finished: Vec<Frame>,
+ lines: Vec<(Length, Frame, Align)>,
lines_size: Gen<Length>,
- run: Vec<(Length, BoxLayout, Align)>,
+ run: Vec<(Length, Frame, Align)>,
run_size: Gen<Length>,
run_ruler: Align,
}
impl<'a> ParLayouter<'a> {
- fn new(par: &'a Par, areas: Areas) -> Self {
+ fn new(par: &'a NodePar, areas: Areas) -> Self {
Self {
par,
- main: par.flow.main.axis(),
- cross: par.flow.cross.axis(),
- flow: par.flow,
+ main: par.dirs.main.axis(),
+ cross: par.dirs.cross.axis(),
+ dirs: par.dirs,
areas,
finished: vec![],
lines: vec![],
@@ -80,7 +78,7 @@ impl<'a> ParLayouter<'a> {
self.run_size.cross = (self.run_size.cross + amount).min(cross_max);
}
- fn push_layout(&mut self, layout: BoxLayout, align: Align) {
+ fn push_frame(&mut self, frame: Frame, align: Align) {
if self.run_ruler > align {
self.finish_run();
}
@@ -88,16 +86,16 @@ impl<'a> ParLayouter<'a> {
let fits = {
let mut usable = self.areas.current.rem;
*usable.get_mut(self.cross) -= self.run_size.cross;
- usable.fits(layout.size)
+ usable.fits(frame.size)
};
if !fits {
self.finish_run();
- while !self.areas.current.rem.fits(layout.size) {
+ while !self.areas.current.rem.fits(frame.size) {
if self.areas.in_full_last() {
// TODO: Diagnose once the necessary spans exist.
- let _ = warning!("cannot fit box into any area");
+ let _ = warning!("cannot fit frame into any area");
break;
} else {
self.finish_area();
@@ -105,8 +103,8 @@ impl<'a> ParLayouter<'a> {
}
}
- let size = layout.size.switch(self.flow);
- self.run.push((self.run_size.cross, layout, align));
+ let size = frame.size.switch(self.dirs);
+ self.run.push((self.run_size.cross, frame, align));
self.run_size.cross += size.cross;
self.run_size.main = self.run_size.main.max(size.main);
@@ -119,13 +117,13 @@ impl<'a> ParLayouter<'a> {
Expansion::Fit => self.run_size.cross,
});
- let mut output = BoxLayout::new(full_size.switch(self.flow).to_size());
+ let mut output = Frame::new(full_size.switch(self.dirs).to_size());
- for (before, layout, align) in std::mem::take(&mut self.run) {
- let child_cross_size = layout.size.get(self.cross);
+ for (before, frame, align) in std::mem::take(&mut self.run) {
+ let child_cross_size = frame.size.get(self.cross);
// Position along the cross axis.
- let cross = align.resolve(if self.flow.cross.is_positive() {
+ let cross = align.resolve(if self.dirs.cross.is_positive() {
let after_with_self = self.run_size.cross - before;
before .. full_size.cross - after_with_self
} else {
@@ -134,8 +132,8 @@ impl<'a> ParLayouter<'a> {
full_size.cross - before_with_self .. after
});
- let pos = Gen::new(Length::ZERO, cross).switch(self.flow).to_point();
- output.push_layout(pos, layout);
+ let pos = Gen::new(Length::ZERO, cross).switch(self.dirs).to_point();
+ output.push_frame(pos, frame);
}
self.lines.push((self.lines_size.main, output, self.run_ruler));
@@ -151,27 +149,27 @@ impl<'a> ParLayouter<'a> {
fn finish_area(&mut self) {
let size = self.lines_size;
- let mut output = BoxLayout::new(size.switch(self.flow).to_size());
+ let mut output = Frame::new(size.switch(self.dirs).to_size());
for (before, run, cross_align) in std::mem::take(&mut self.lines) {
- let child_size = run.size.switch(self.flow);
+ let child_size = run.size.switch(self.dirs);
// Position along the main axis.
- let main = if self.flow.main.is_positive() {
+ let main = if self.dirs.main.is_positive() {
before
} else {
size.main - (before + child_size.main)
};
// Align along the cross axis.
- let cross = cross_align.resolve(if self.flow.cross.is_positive() {
+ let cross = cross_align.resolve(if self.dirs.cross.is_positive() {
Length::ZERO .. size.cross - child_size.cross
} else {
size.cross - child_size.cross .. Length::ZERO
});
- let pos = Gen::new(main, cross).switch(self.flow).to_point();
- output.push_layout(pos, run);
+ let pos = Gen::new(main, cross).switch(self.dirs).to_point();
+ output.push_frame(pos, run);
}
self.finished.push(output);
@@ -180,7 +178,7 @@ impl<'a> ParLayouter<'a> {
self.lines_size = Gen::ZERO;
}
- fn finish(mut self) -> Vec<BoxLayout> {
+ fn finish(mut self) -> Vec<Frame> {
self.finish_run();
self.finish_area();
self.finished
diff --git a/src/layout/spacing.rs b/src/layout/spacing.rs
index c9a9c233..f0024fab 100644
--- a/src/layout/spacing.rs
+++ b/src/layout/spacing.rs
@@ -5,7 +5,7 @@ use crate::eval::Softness;
/// A spacing node.
#[derive(Copy, Clone, PartialEq)]
-pub struct Spacing {
+pub struct NodeSpacing {
/// The amount of spacing to insert.
pub amount: Length,
/// Defines how spacing interacts with surrounding spacing.
@@ -19,13 +19,13 @@ pub struct Spacing {
pub softness: Softness,
}
-impl Layout for Spacing {
+impl Layout for NodeSpacing {
fn layout(&self, _: &mut LayoutContext, _: &Areas) -> Layouted {
Layouted::Spacing(self.amount)
}
}
-impl Debug for Spacing {
+impl Debug for NodeSpacing {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self.softness {
Softness::Soft => write!(f, "Soft({})", self.amount),
@@ -34,8 +34,8 @@ impl Debug for Spacing {
}
}
-impl From<Spacing> for LayoutNode {
- fn from(spacing: Spacing) -> Self {
+impl From<NodeSpacing> for Node {
+ fn from(spacing: NodeSpacing) -> Self {
Self::Spacing(spacing)
}
}
diff --git a/src/layout/stack.rs b/src/layout/stack.rs
index 9d2540e9..e98be7ed 100644
--- a/src/layout/stack.rs
+++ b/src/layout/stack.rs
@@ -1,65 +1,65 @@
use super::*;
-/// A node that stacks and align its children.
+/// A node that stacks its children.
#[derive(Debug, Clone, PartialEq)]
-pub struct Stack {
+pub struct NodeStack {
/// The `main` and `cross` directions of this stack.
///
/// The children are stacked along the `main` direction. The `cross`
/// direction is required for aligning the children.
- pub flow: Flow,
+ pub dirs: LayoutDirs,
/// How to align this stack in _its_ parent.
- pub align: BoxAlign,
+ pub align: ChildAlign,
/// Whether to expand the axes to fill the area or to fit the content.
pub expansion: Gen<Expansion>,
/// The nodes to be stacked.
- pub children: Vec<LayoutNode>,
+ pub children: Vec<Node>,
}
-impl Layout for Stack {
+impl Layout for NodeStack {
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted {
let mut layouter = StackLayouter::new(self, areas.clone());
for child in &self.children {
match child.layout(ctx, &layouter.areas) {
Layouted::Spacing(spacing) => layouter.push_spacing(spacing),
- Layouted::Layout(layout, align) => layouter.push_layout(layout, align),
- Layouted::Layouts(layouts, align) => {
- for layout in layouts {
- layouter.push_layout(layout, align);
+ Layouted::Frame(frame, align) => layouter.push_frame(frame, align),
+ Layouted::Frames(frames, align) => {
+ for frame in frames {
+ layouter.push_frame(frame, align);
}
}
}
}
- Layouted::Layouts(layouter.finish(), self.align)
+ Layouted::Frames(layouter.finish(), self.align)
}
}
-impl From<Stack> for LayoutNode {
- fn from(stack: Stack) -> Self {
- Self::dynamic(stack)
+impl From<NodeStack> for Node {
+ fn from(stack: NodeStack) -> Self {
+ Self::any(stack)
}
}
struct StackLayouter<'a> {
- stack: &'a Stack,
+ stack: &'a NodeStack,
main: SpecAxis,
- flow: Flow,
+ dirs: LayoutDirs,
areas: Areas,
- finished: Vec<BoxLayout>,
- layouts: Vec<(Length, BoxLayout, BoxAlign)>,
+ finished: Vec<Frame>,
+ frames: Vec<(Length, Frame, ChildAlign)>,
used: Gen<Length>,
ruler: Align,
}
impl<'a> StackLayouter<'a> {
- fn new(stack: &'a Stack, areas: Areas) -> Self {
+ fn new(stack: &'a NodeStack, areas: Areas) -> Self {
Self {
stack,
- main: stack.flow.main.axis(),
- flow: stack.flow,
+ main: stack.dirs.main.axis(),
+ dirs: stack.dirs,
areas,
finished: vec![],
- layouts: vec![],
+ frames: vec![],
used: Gen::ZERO,
ruler: Align::Start,
}
@@ -72,23 +72,23 @@ impl<'a> StackLayouter<'a> {
self.used.main += capped;
}
- fn push_layout(&mut self, layout: BoxLayout, align: BoxAlign) {
+ fn push_frame(&mut self, frame: Frame, align: ChildAlign) {
if self.ruler > align.main {
self.finish_area();
}
- while !self.areas.current.rem.fits(layout.size) {
+ while !self.areas.current.rem.fits(frame.size) {
if self.areas.in_full_last() {
// TODO: Diagnose once the necessary spans exist.
- let _ = warning!("cannot fit box into any area");
+ let _ = warning!("cannot fit frame into any area");
break;
} else {
self.finish_area();
}
}
- let size = layout.size.switch(self.flow);
- self.layouts.push((self.used.main, layout, align));
+ let size = frame.size.switch(self.dirs);
+ self.frames.push((self.used.main, frame, align));
*self.areas.current.rem.get_mut(self.main) -= size.main;
self.used.main += size.main;
@@ -98,7 +98,7 @@ impl<'a> StackLayouter<'a> {
fn finish_area(&mut self) {
let full_size = {
- let full = self.areas.current.full.switch(self.flow);
+ let full = self.areas.current.full.switch(self.dirs);
Gen::new(
match self.stack.expansion.main {
Expansion::Fill => full.main,
@@ -111,13 +111,13 @@ impl<'a> StackLayouter<'a> {
)
};
- let mut output = BoxLayout::new(full_size.switch(self.flow).to_size());
+ let mut output = Frame::new(full_size.switch(self.dirs).to_size());
- for (before, layout, align) in std::mem::take(&mut self.layouts) {
- let child_size = layout.size.switch(self.flow);
+ for (before, frame, align) in std::mem::take(&mut self.frames) {
+ let child_size = frame.size.switch(self.dirs);
// Align along the main axis.
- let main = align.main.resolve(if self.flow.main.is_positive() {
+ let main = align.main.resolve(if self.dirs.main.is_positive() {
let after_with_self = self.used.main - before;
before .. full_size.main - after_with_self
} else {
@@ -127,14 +127,14 @@ impl<'a> StackLayouter<'a> {
});
// Align along the cross axis.
- let cross = align.cross.resolve(if self.flow.cross.is_positive() {
+ let cross = align.cross.resolve(if self.dirs.cross.is_positive() {
Length::ZERO .. full_size.cross - child_size.cross
} else {
full_size.cross - child_size.cross .. Length::ZERO
});
- let pos = Gen::new(main, cross).switch(self.flow).to_point();
- output.push_layout(pos, layout);
+ let pos = Gen::new(main, cross).switch(self.dirs).to_point();
+ output.push_frame(pos, frame);
}
self.finished.push(output);
@@ -144,7 +144,7 @@ impl<'a> StackLayouter<'a> {
self.ruler = Align::Start;
}
- fn finish(mut self) -> Vec<BoxLayout> {
+ fn finish(mut self) -> Vec<Frame> {
self.finish_area();
self.finished
}
diff --git a/src/layout/text.rs b/src/layout/text.rs
index 56b2328e..cfd83372 100644
--- a/src/layout/text.rs
+++ b/src/layout/text.rs
@@ -8,11 +8,11 @@ use crate::shaping;
/// A text node.
#[derive(Clone, PartialEq)]
-pub struct Text {
+pub struct NodeText {
/// The text.
pub text: String,
/// How to align this text node in its parent.
- pub align: BoxAlign,
+ pub align: ChildAlign,
/// The text direction.
pub dir: Dir,
/// The font size.
@@ -23,15 +23,15 @@ pub struct Text {
pub variant: FontVariant,
}
-impl Layout for Text {
+impl Layout for NodeText {
fn layout(&self, ctx: &mut LayoutContext, _: &Areas) -> Layouted {
let mut env = ctx.env.borrow_mut();
- Layouted::Layout(
+ Layouted::Frame(
shaping::shape(
- &mut env.fonts,
&self.text,
self.dir,
self.font_size,
+ &mut env.fonts,
&self.families,
self.variant,
),
@@ -40,14 +40,14 @@ impl Layout for Text {
}
}
-impl Debug for Text {
+impl Debug for NodeText {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "Text({})", self.text)
}
}
-impl From<Text> for LayoutNode {
- fn from(text: Text) -> Self {
+impl From<NodeText> for Node {
+ fn from(text: NodeText) -> Self {
Self::Text(text)
}
}
diff --git a/src/lib.rs b/src/lib.rs
index d471b09d..15d68803 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -5,23 +5,23 @@
//! [iterator of tokens][tokens]. This token stream is [parsed] into a [syntax
//! tree]. The structures describing the tree can be found in the [syntax]
//! module.
-//! - **Evaluation:** The next step is to [evaluate] the parsed "script" to a
-//! [document], a high-level, fully styled representation. The nodes of the
-//! document tree are fully self-contained and order-independent and thus much
+//! - **Evaluation:** The next step is to [evaluate] the parsed "script" into a
+//! [layout tree], a high-level, fully styled representation. The nodes of
+//! this tree are fully self-contained and order-independent and thus much
//! better suited for layouting than the syntax tree.
-//! - **Layouting:** The next step is to [layout] the document into a portable
-//! version of the typeset document. The output of this is a vector of
-//! [`BoxLayout`]s (corresponding to pages), ready for exporting.
+//! - **Layouting:** Next, the tree is to [layouted] into a portable version of
+//! the typeset document. The output of this is a vector of [`Frame`]s
+//! (corresponding to pages), ready for exporting.
//! - **Exporting:** The finished layout can be exported into a supported
//! format. Submodules for these formats are located in the [export] module.
//! Currently, the only supported output format is [_PDF_].
//!
//! [tokens]: parse::Tokens
//! [parsed]: parse::parse
-//! [syntax tree]: syntax::SynTree
+//! [syntax tree]: syntax::Tree
//! [evaluate]: eval::eval
-//! [document]: layout::Document
-//! [layout]: layout::layout
+//! [layout tree]: layout::Tree
+//! [layouted]: layout::layout
//! [_PDF_]: export::pdf
#[macro_use]
@@ -46,13 +46,13 @@ use std::rc::Rc;
use crate::diag::{Feedback, Pass};
use crate::env::SharedEnv;
use crate::eval::State;
-use crate::layout::BoxLayout;
+use crate::layout::Frame;
-/// Process _Typst_ source code directly into a collection of layouts.
-pub fn typeset(src: &str, env: SharedEnv, state: State) -> Pass<Vec<BoxLayout>> {
- let Pass { output: tree, feedback: f1 } = parse::parse(src);
- let Pass { output: document, feedback: f2 } =
- eval::eval(&tree, Rc::clone(&env), state);
- let layouts = layout::layout(&document, env);
- Pass::new(layouts, Feedback::join(f1, f2))
+/// Process _Typst_ source code directly into a collection of frames.
+pub fn typeset(src: &str, env: SharedEnv, state: State) -> Pass<Vec<Frame>> {
+ let Pass { output: syntax_tree, feedback: f1 } = parse::parse(src);
+ let Pass { output: layout_tree, feedback: f2 } =
+ eval::eval(&syntax_tree, Rc::clone(&env), state);
+ let frames = layout::layout(&layout_tree, env);
+ Pass::new(frames, Feedback::join(f1, f2))
}
diff --git a/src/library/insert.rs b/src/library/insert.rs
index 587f96dc..7d95afe3 100644
--- a/src/library/insert.rs
+++ b/src/library/insert.rs
@@ -22,7 +22,7 @@ pub fn image(ctx: &mut EvalContext, args: &mut Args) -> Value {
if let Some((res, img)) = loaded {
let dimensions = img.buf.dimensions();
drop(env);
- ctx.push(Image {
+ ctx.push(NodeImage {
res,
dimensions,
width,
@@ -40,7 +40,7 @@ pub fn image(ctx: &mut EvalContext, args: &mut Args) -> Value {
/// An image node.
#[derive(Debug, Clone, PartialEq)]
-struct Image {
+struct NodeImage {
/// The resource id of the image file.
res: ResourceId,
/// The pixel dimensions of the image.
@@ -50,10 +50,10 @@ struct Image {
/// The fixed height, if any.
height: Option<Linear>,
/// How to align this image node in its parent.
- align: BoxAlign,
+ align: ChildAlign,
}
-impl Layout for Image {
+impl Layout for NodeImage {
fn layout(&self, _: &mut LayoutContext, areas: &Areas) -> Layouted {
let Area { rem, full } = areas.current;
let pixel_ratio = (self.dimensions.0 as f64) / (self.dimensions.1 as f64);
@@ -76,18 +76,15 @@ impl Layout for Image {
}
};
- let mut boxed = BoxLayout::new(size);
- boxed.push(
- Point::ZERO,
- LayoutElement::Image(ImageElement { res: self.res, size }),
- );
+ let mut frame = Frame::new(size);
+ frame.push(Point::ZERO, Element::Image(Image { res: self.res, size }));
- Layouted::Layout(boxed, self.align)
+ Layouted::Frame(frame, self.align)
}
}
-impl From<Image> for LayoutNode {
- fn from(image: Image) -> Self {
- Self::dynamic(image)
+impl From<NodeImage> for Node {
+ fn from(image: NodeImage) -> Self {
+ Self::any(image)
}
}
diff --git a/src/library/layout.rs b/src/library/layout.rs
index ac152d07..e469c9be 100644
--- a/src/library/layout.rs
+++ b/src/library/layout.rs
@@ -1,5 +1,5 @@
use crate::eval::Softness;
-use crate::layout::{Expansion, Fixed, Spacing, Stack};
+use crate::layout::{Expansion, NodeFixed, NodeSpacing, NodeStack};
use crate::paper::{Paper, PaperClass};
use crate::prelude::*;
@@ -45,8 +45,8 @@ pub fn align(ctx: &mut EvalContext, args: &mut Args) -> Value {
// Check whether we know which axis this alignment belongs to.
if let Some(axis) = axis {
// We know the axis.
- let gen_axis = axis.switch(ctx.state.flow);
- let gen_align = arg.switch(ctx.state.flow);
+ let gen_axis = axis.switch(ctx.state.dirs);
+ let gen_align = arg.switch(ctx.state.dirs);
if arg.axis().map_or(false, |a| a != axis) {
ctx.diag(error!(span, "invalid alignment for {} axis", axis));
@@ -132,7 +132,7 @@ impl Alignment {
impl Switch for Alignment {
type Other = Align;
- fn switch(self, flow: Flow) -> Self::Other {
+ fn switch(self, dirs: LayoutDirs) -> Self::Other {
let get = |dir: Dir, at_positive_start| {
if dir.is_positive() == at_positive_start {
Align::Start
@@ -141,12 +141,12 @@ impl Switch for Alignment {
}
};
- let flow = flow.switch(flow);
+ let dirs = dirs.switch(dirs);
match self {
- Self::Left => get(flow.horizontal, true),
- Self::Right => get(flow.horizontal, false),
- Self::Top => get(flow.vertical, true),
- Self::Bottom => get(flow.vertical, false),
+ Self::Left => get(dirs.horizontal, true),
+ Self::Right => get(dirs.horizontal, false),
+ Self::Top => get(dirs.vertical, true),
+ Self::Bottom => get(dirs.vertical, false),
Self::Center => Align::Center,
}
}
@@ -169,9 +169,9 @@ pub fn boxed(ctx: &mut EvalContext, args: &mut Args) -> Value {
let main = args.get(ctx, "main-dir");
let cross = args.get(ctx, "cross-dir");
- ctx.set_flow(Gen::new(main, cross));
+ ctx.set_dirs(Gen::new(main, cross));
- let flow = ctx.state.flow;
+ let dirs = ctx.state.dirs;
let align = ctx.state.align;
ctx.start_content_group();
@@ -182,19 +182,14 @@ pub fn boxed(ctx: &mut EvalContext, args: &mut Args) -> Value {
let children = ctx.end_content_group();
- ctx.push(Fixed {
+ let fill_if = |c| if c { Expansion::Fill } else { Expansion::Fit };
+ let expansion =
+ Spec::new(fill_if(width.is_some()), fill_if(height.is_some())).switch(dirs);
+
+ ctx.push(NodeFixed {
width,
height,
- child: LayoutNode::dynamic(Stack {
- flow,
- align,
- expansion: Spec::new(
- Expansion::fill_if(width.is_some()),
- Expansion::fill_if(height.is_some()),
- )
- .switch(flow),
- children,
- }),
+ child: Node::any(NodeStack { dirs, align, expansion, children }),
});
ctx.state = snapshot;
@@ -227,8 +222,8 @@ fn spacing(ctx: &mut EvalContext, args: &mut Args, axis: SpecAxis) -> Value {
if let Some(linear) = spacing {
let amount = linear.resolve(ctx.state.font.font_size());
- let spacing = Spacing { amount, softness: Softness::Hard };
- if axis == ctx.state.flow.main.axis() {
+ let spacing = NodeSpacing { amount, softness: Softness::Hard };
+ if axis == ctx.state.dirs.main.axis() {
ctx.end_par_group();
ctx.push(spacing);
ctx.start_par_group();
@@ -305,7 +300,7 @@ pub fn page(ctx: &mut EvalContext, args: &mut Args) -> Value {
let main = args.get(ctx, "main-dir");
let cross = args.get(ctx, "cross-dir");
- ctx.set_flow(Gen::new(main, cross));
+ ctx.set_dirs(Gen::new(main, cross));
let mut softness = ctx.end_page_group(|_| false);
if let Some(body) = args.find::<ValueContent>(ctx) {
diff --git a/src/main.rs b/src/main.rs
index 213734d7..4746bc0c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -49,7 +49,7 @@ fn main() -> anyhow::Result<()> {
let state = State::default();
let Pass {
- output: layouts,
+ output: frames,
feedback: Feedback { mut diags, .. },
} = typeset(&src, Rc::clone(&env), state);
@@ -72,7 +72,7 @@ fn main() -> anyhow::Result<()> {
}
}
- let pdf_data = pdf::export(&layouts, &env.borrow());
+ let pdf_data = pdf::export(&frames, &env.borrow());
fs::write(&dest_path, pdf_data).context("Failed to write PDF file.")?;
Ok(())
diff --git a/src/paper.rs b/src/paper.rs
index 21f54756..bbe07803 100644
--- a/src/paper.rs
+++ b/src/paper.rs
@@ -5,7 +5,7 @@ use crate::geom::{Length, Linear, Relative, Sides, Size};
/// Specification of a paper.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Paper {
- /// The kind of paper, which defines the default margins.
+ /// The broad class this paper belongs to.
pub class: PaperClass,
/// The width of the paper in millimeters.
pub width: f64,
@@ -25,7 +25,7 @@ impl Paper {
}
}
-/// Paper classes define default margins for a class of related papers.
+/// Defines default margins for a class of related papers.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum PaperClass {
Custom,
diff --git a/src/parse/collection.rs b/src/parse/collection.rs
index db267dbe..889cfb0f 100644
--- a/src/parse/collection.rs
+++ b/src/parse/collection.rs
@@ -2,7 +2,7 @@ use super::*;
use crate::diag::Deco;
/// Parse the arguments to a function call.
-pub fn arguments(p: &mut Parser) -> Arguments {
+pub fn arguments(p: &mut Parser) -> ExprArgs {
collection(p, vec![])
}
@@ -74,7 +74,7 @@ trait Collection {
fn push_comma(&mut self) {}
}
-impl Collection for Arguments {
+impl Collection for ExprArgs {
fn push_arg(&mut self, _: &mut Parser, arg: Spanned<Argument>) {
self.push(arg.v);
}
@@ -85,17 +85,17 @@ impl Collection for Arguments {
enum State {
Unknown,
Expr(Spanned<Expr>),
- Array(Array),
- Dict(Dict),
+ Array(ExprArray),
+ Dict(ExprDict),
}
impl State {
fn into_expr(self) -> Expr {
match self {
- Self::Unknown => Expr::Lit(Lit::Array(vec![])),
+ Self::Unknown => Expr::Array(vec![]),
Self::Expr(expr) => expr.v,
- Self::Array(array) => Expr::Lit(Lit::Array(array)),
- Self::Dict(dict) => Expr::Lit(Lit::Dict(dict)),
+ Self::Array(array) => Expr::Array(array),
+ Self::Dict(dict) => Expr::Dict(dict),
}
}
}
diff --git a/src/parse/lines.rs b/src/parse/lines.rs
index be120d8a..d1a6c781 100644
--- a/src/parse/lines.rs
+++ b/src/parse/lines.rs
@@ -1,5 +1,3 @@
-//! Conversion of byte positions to line/column locations.
-
use super::Scanner;
use crate::syntax::{Location, Offset, Pos};
diff --git a/src/parse/mod.rs b/src/parse/mod.rs
index 912a34d0..e6cac17f 100644
--- a/src/parse/mod.rs
+++ b/src/parse/mod.rs
@@ -22,13 +22,13 @@ use crate::syntax::*;
use collection::{arguments, parenthesized};
/// Parse a string of source code.
-pub fn parse(src: &str) -> Pass<SynTree> {
+pub fn parse(src: &str) -> Pass<Tree> {
let mut p = Parser::new(src);
Pass::new(tree(&mut p), p.finish())
}
/// Parse a syntax tree.
-fn tree(p: &mut Parser) -> SynTree {
+fn tree(p: &mut Parser) -> Tree {
// We keep track of whether we are at the start of a block or paragraph
// to know whether headings are allowed.
let mut at_start = true;
@@ -36,8 +36,8 @@ fn tree(p: &mut Parser) -> SynTree {
while !p.eof() {
if let Some(node) = p.span_if(|p| node(p, at_start)) {
match node.v {
- SynNode::Parbreak => at_start = true,
- SynNode::Space => {}
+ Node::Parbreak => at_start = true,
+ Node::Space => {}
_ => at_start = false,
}
tree.push(node);
@@ -47,42 +47,42 @@ fn tree(p: &mut Parser) -> SynTree {
}
/// Parse a syntax node.
-fn node(p: &mut Parser, at_start: bool) -> Option<SynNode> {
+fn node(p: &mut Parser, at_start: bool) -> Option<Node> {
let node = match p.peek()? {
Token::Space(newlines) => {
if newlines < 2 {
- SynNode::Space
+ Node::Space
} else {
- SynNode::Parbreak
+ Node::Parbreak
}
}
- Token::Text(text) => SynNode::Text(text.into()),
+ Token::Text(text) => Node::Text(text.into()),
Token::LineComment(_) | Token::BlockComment(_) => {
p.eat();
return None;
}
- Token::Star => SynNode::Strong,
- Token::Underscore => SynNode::Emph,
- Token::Tilde => SynNode::Text("\u{00A0}".into()),
- Token::Backslash => SynNode::Linebreak,
+ Token::Star => Node::Strong,
+ Token::Underscore => Node::Emph,
+ Token::Tilde => Node::Text("\u{00A0}".into()),
+ Token::Backslash => Node::Linebreak,
Token::Hashtag => {
if at_start {
- return Some(SynNode::Heading(heading(p)));
+ return Some(Node::Heading(heading(p)));
} else {
- SynNode::Text(p.get(p.peek_span()).into())
+ Node::Text(p.get(p.peek_span()).into())
}
}
- Token::Raw(t) => SynNode::Raw(raw(p, t)),
- Token::UnicodeEscape(t) => SynNode::Text(unicode_escape(p, t)),
+ Token::Raw(t) => Node::Raw(raw(p, t)),
+ Token::UnicodeEscape(t) => Node::Text(unicode_escape(p, t)),
Token::LeftBracket => {
- return Some(SynNode::Expr(Expr::Call(bracket_call(p))));
+ return Some(Node::Expr(Expr::Call(bracket_call(p))));
}
Token::LeftBrace => {
- return Some(SynNode::Expr(block_expr(p)?));
+ return Some(Node::Expr(block_expr(p)?));
}
_ => {
@@ -189,15 +189,15 @@ fn bracket_call(p: &mut Parser) -> ExprCall {
p.end_group();
if p.peek() == Some(Token::LeftBracket) {
- let body = p.span(|p| Expr::Lit(Lit::Content(bracket_body(p))));
+ let body = p.span(|p| Expr::Content(bracket_body(p)));
inner.span.expand(body.span);
inner.v.args.v.push(Argument::Pos(body));
}
while let Some(mut top) = outer.pop() {
let span = inner.span;
- let node = inner.map(|c| SynNode::Expr(Expr::Call(c)));
- let expr = Expr::Lit(Lit::Content(vec![node])).with_span(span);
+ let node = inner.map(|c| Node::Expr(Expr::Call(c)));
+ let expr = Expr::Content(vec![node]).with_span(span);
top.v.args.v.push(Argument::Pos(expr));
inner = top;
}
@@ -227,7 +227,7 @@ fn bracket_subheader(p: &mut Parser) -> ExprCall {
}
/// Parse the body of a bracketed function call.
-fn bracket_body(p: &mut Parser) -> SynTree {
+fn bracket_body(p: &mut Parser) -> Tree {
p.push_mode(TokenMode::Body);
p.start_group(Group::Bracket);
let tree = tree(p);
@@ -299,13 +299,13 @@ fn value(p: &mut Parser) -> Option<Expr> {
let expr = match p.peek() {
// Bracketed function call.
Some(Token::LeftBracket) => {
- let node = p.span(|p| SynNode::Expr(Expr::Call(bracket_call(p))));
- return Some(Expr::Lit(Lit::Content(vec![node])));
+ let node = p.span(|p| Node::Expr(Expr::Call(bracket_call(p))));
+ return Some(Expr::Content(vec![node]));
}
// Content expression.
Some(Token::LeftBrace) => {
- return Some(Expr::Lit(Lit::Content(content(p))));
+ return Some(Expr::Content(content(p)));
}
// Dictionary or just a parenthesized expression.
@@ -345,7 +345,7 @@ fn value(p: &mut Parser) -> Option<Expr> {
}
// Parse a content value: `{...}`.
-fn content(p: &mut Parser) -> SynTree {
+fn content(p: &mut Parser) -> Tree {
p.push_mode(TokenMode::Body);
p.start_group(Group::Brace);
let tree = tree(p);
diff --git a/src/parse/resolve.rs b/src/parse/resolve.rs
index 051bc7d5..d6c6d8a4 100644
--- a/src/parse/resolve.rs
+++ b/src/parse/resolve.rs
@@ -1,5 +1,3 @@
-//! Resolve strings and raw blocks.
-
use super::{is_newline, Scanner};
use crate::syntax::{Ident, NodeRaw};
diff --git a/src/parse/scanner.rs b/src/parse/scanner.rs
index 69ad2138..cc23a612 100644
--- a/src/parse/scanner.rs
+++ b/src/parse/scanner.rs
@@ -1,9 +1,7 @@
-//! Low-level char-based scanner.
-
use std::fmt::{self, Debug, Formatter};
use std::slice::SliceIndex;
-/// A low-level featureful char-based scanner.
+/// A featureful char-based scanner.
#[derive(Clone)]
pub struct Scanner<'s> {
src: &'s str,
diff --git a/src/parse/tests.rs b/src/parse/tests.rs
index 0c8998b5..d01d09a5 100644
--- a/src/parse/tests.rs
+++ b/src/parse/tests.rs
@@ -9,7 +9,7 @@ use crate::geom::Unit;
use crate::syntax::*;
use BinOp::*;
-use SynNode::{Emph, Linebreak, Parbreak, Space, Strong};
+use Node::{Emph, Linebreak, Parbreak, Space, Strong};
use UnOp::*;
macro_rules! t {
@@ -82,16 +82,16 @@ macro_rules! into {
};
}
-fn Text(text: &str) -> SynNode {
- SynNode::Text(text.into())
+fn Text(text: &str) -> Node {
+ Node::Text(text.into())
}
-fn Heading(level: impl Into<Spanned<u8>>, contents: SynTree) -> SynNode {
- SynNode::Heading(NodeHeading { level: level.into(), contents })
+fn Heading(level: impl Into<Spanned<u8>>, contents: Tree) -> Node {
+ Node::Heading(NodeHeading { level: level.into(), contents })
}
-fn Raw(lang: Option<&str>, lines: &[&str], inline: bool) -> SynNode {
- SynNode::Raw(NodeRaw {
+fn Raw(lang: Option<&str>, lines: &[&str], inline: bool) -> Node {
+ Node::Raw(NodeRaw {
lang: lang.map(|id| Ident(id.into())),
lines: lines.iter().map(ToString::to_string).collect(),
inline,
@@ -130,8 +130,8 @@ fn Str(string: &str) -> Expr {
Expr::Lit(Lit::Str(string.to_string()))
}
-fn Block(expr: Expr) -> SynNode {
- SynNode::Expr(expr)
+fn Block(expr: Expr) -> Node {
+ Node::Expr(expr)
}
fn Binary(
@@ -157,7 +157,7 @@ macro_rules! Array {
(@$($expr:expr),* $(,)?) => {
vec![$(into!($expr)),*]
};
- ($($tts:tt)*) => (Expr::Lit(Lit::Array(Array![@$($tts)*])));
+ ($($tts:tt)*) => (Expr::Array(Array![@$($tts)*]));
}
macro_rules! Dict {
@@ -167,7 +167,7 @@ macro_rules! Dict {
expr: into!($expr)
}),*]
};
- ($($tts:tt)*) => (Expr::Lit(Lit::Dict(Dict![@$($tts)*])));
+ ($($tts:tt)*) => (Expr::Dict(Dict![@$($tts)*]));
}
macro_rules! Args {
@@ -187,7 +187,7 @@ macro_rules! Args {
macro_rules! Content {
(@$($node:expr),* $(,)?) => (vec![$(into!($node)),*]);
- ($($tts:tt)*) => (Expr::Lit(Lit::Content(Content![@$($tts)*])));
+ ($($tts:tt)*) => (Expr::Content(Content![@$($tts)*]));
}
macro_rules! Call {
@@ -201,7 +201,7 @@ macro_rules! Call {
}
};
(@$($tts:tt)*) => (Expr::Call(Call!(@@$($tts)*)));
- ($($tts:tt)*) => (SynNode::Expr(Call!(@$($tts)*)));
+ ($($tts:tt)*) => (Node::Expr(Call!(@$($tts)*)));
}
#[test]
diff --git a/src/parse/tokens.rs b/src/parse/tokens.rs
index a9692a58..ff7f11bd 100644
--- a/src/parse/tokens.rs
+++ b/src/parse/tokens.rs
@@ -1,5 +1,3 @@
-//! Tokenization.
-
use std::fmt::{self, Debug, Formatter};
use super::{is_newline, Scanner};
diff --git a/src/prelude.rs b/src/prelude.rs
index 5d446e8c..1db0d5b0 100644
--- a/src/prelude.rs
+++ b/src/prelude.rs
@@ -8,7 +8,7 @@ pub use crate::eval::{
};
pub use crate::geom::*;
#[doc(no_inline)]
-pub use crate::layout::LayoutNode;
+pub use crate::layout::Node;
#[doc(no_inline)]
-pub use crate::syntax::{Span, Spanned, SynTree, WithSpan};
-pub use crate::{error, warning};
+pub use crate::syntax::{Span, Spanned, WithSpan};
+pub use crate::{error, impl_type, warning};
diff --git a/src/shaping.rs b/src/shaping.rs
index c42cd0ac..722d103a 100644
--- a/src/shaping.rs
+++ b/src/shaping.rs
@@ -11,7 +11,7 @@ use ttf_parser::{Face, GlyphId};
use crate::font::FontLoader;
use crate::geom::{Dir, Length, Point, Size};
-use crate::layout::{BoxLayout, LayoutElement};
+use crate::layout::{Element, Frame};
/// A shaped run of text.
#[derive(Clone, PartialEq)]
@@ -31,13 +31,13 @@ pub struct Shaped {
impl Shaped {
/// Create a new shape run with empty `text`, `glyphs` and `offsets`.
- pub fn new(face: FaceId, size: Length) -> Self {
+ pub fn new(face: FaceId, font_size: Length) -> Self {
Self {
text: String::new(),
face,
glyphs: vec![],
offsets: vec![],
- font_size: size,
+ font_size,
}
}
@@ -58,16 +58,16 @@ impl Debug for Shaped {
}
}
-/// Shape text into a box containing [`Shaped`] runs.
+/// Shape text into a frame containing [`Shaped`] runs.
pub fn shape(
- loader: &mut FontLoader,
text: &str,
dir: Dir,
font_size: Length,
+ loader: &mut FontLoader,
fallback: &FallbackTree,
variant: FontVariant,
-) -> BoxLayout {
- let mut layout = BoxLayout::new(Size::new(Length::ZERO, font_size));
+) -> Frame {
+ let mut frame = Frame::new(Size::new(Length::ZERO, font_size));
let mut shaped = Shaped::new(FaceId::MAX, font_size);
let mut offset = Length::ZERO;
@@ -91,9 +91,9 @@ pub fn shape(
// Flush the buffer if we change the font face.
if shaped.face != id && !shaped.text.is_empty() {
- let pos = Point::new(layout.size.width, Length::ZERO);
- layout.push(pos, LayoutElement::Text(shaped));
- layout.size.width += offset;
+ let pos = Point::new(frame.size.width, Length::ZERO);
+ frame.push(pos, Element::Text(shaped));
+ frame.size.width += offset;
shaped = Shaped::new(FaceId::MAX, font_size);
offset = Length::ZERO;
}
@@ -108,12 +108,12 @@ pub fn shape(
// Flush the last buffered parts of the word.
if !shaped.text.is_empty() {
- let pos = Point::new(layout.size.width, Length::ZERO);
- layout.push(pos, LayoutElement::Text(shaped));
- layout.size.width += offset;
+ let pos = Point::new(frame.size.width, Length::ZERO);
+ frame.push(pos, Element::Text(shaped));
+ frame.size.width += offset;
}
- layout
+ frame
}
/// Looks up the glyph for `c` and returns its index alongside its width at the
diff --git a/src/syntax/expr.rs b/src/syntax/expr.rs
index 905ade04..94d07b07 100644
--- a/src/syntax/expr.rs
+++ b/src/syntax/expr.rs
@@ -1,5 +1,3 @@
-//! Expressions.
-
use super::*;
use crate::color::RgbaColor;
use crate::geom::Unit;
@@ -7,7 +5,7 @@ use crate::geom::Unit;
/// An expression.
#[derive(Debug, Clone, PartialEq)]
pub enum Expr {
- /// A literal: `true`, `1cm`, `"hi"`, `{_Hey!_}`.
+ /// A literal: `true`, `1cm`, `"hi"`.
Lit(Lit),
/// An invocation of a function: `[foo ...]`, `foo(...)`.
Call(ExprCall),
@@ -15,6 +13,12 @@ pub enum Expr {
Unary(ExprUnary),
/// A binary operation: `a + b`, `a / b`.
Binary(ExprBinary),
+ /// An array expression: `(1, "hi", 12cm)`.
+ Array(ExprArray),
+ /// A dictionary expression: `(color: #f79143, pattern: dashed)`.
+ Dict(ExprDict),
+ /// A content expression: `{*Hello* there!}`.
+ Content(ExprContent),
}
/// An invocation of a function: `[foo ...]`, `foo(...)`.
@@ -23,14 +27,14 @@ pub struct ExprCall {
/// The name of the function.
pub name: Spanned<Ident>,
/// The arguments to the function.
- pub args: Spanned<Arguments>,
+ pub args: Spanned<ExprArgs>,
}
/// The arguments to a function: `12, draw: false`.
///
/// In case of a bracketed invocation with a body, the body is _not_
/// included in the span for the sake of clearer error messages.
-pub type Arguments = Vec<Argument>;
+pub type ExprArgs = Vec<Argument>;
/// An argument to a function call: `12` or `draw: false`.
#[derive(Debug, Clone, PartialEq)]
@@ -41,6 +45,15 @@ pub enum Argument {
Named(Named),
}
+/// A pair of a name and an expression: `pattern: dashed`.
+#[derive(Debug, Clone, PartialEq)]
+pub struct Named {
+ /// The name: `pattern`.
+ pub name: Spanned<Ident>,
+ /// The right-hand side of the pair: `dashed`.
+ pub expr: Spanned<Expr>,
+}
+
/// A unary operation: `-x`.
#[derive(Debug, Clone, PartialEq)]
pub struct ExprUnary {
@@ -81,6 +94,15 @@ pub enum BinOp {
Div,
}
+/// An array expression: `(1, "hi", 12cm)`.
+pub type ExprArray = SpanVec<Expr>;
+
+/// A dictionary expression: `(color: #f79143, pattern: dashed)`.
+pub type ExprDict = Vec<Named>;
+
+/// A content expression: `{*Hello* there!}`.
+pub type ExprContent = Tree;
+
/// A literal.
#[derive(Debug, Clone, PartialEq)]
pub enum Lit {
@@ -103,25 +125,4 @@ pub enum Lit {
Color(RgbaColor),
/// A string literal: `"hello!"`.
Str(String),
- /// An array literal: `(1, "hi", 12cm)`.
- Array(Array),
- /// A dictionary literal: `(color: #f79143, pattern: dashed)`.
- Dict(Dict),
- /// A content literal: `{*Hello* there!}`.
- Content(SynTree),
-}
-
-/// An array literal: `(1, "hi", 12cm)`.
-pub type Array = SpanVec<Expr>;
-
-/// A dictionary literal: `(color: #f79143, pattern: dashed)`.
-pub type Dict = Vec<Named>;
-
-/// A pair of a name and an expression: `pattern: dashed`.
-#[derive(Debug, Clone, PartialEq)]
-pub struct Named {
- /// The name: `pattern`.
- pub name: Spanned<Ident>,
- /// The right-hand side of the pair: `dashed`.
- pub expr: Spanned<Expr>,
}
diff --git a/src/syntax/ident.rs b/src/syntax/ident.rs
index 4f3668c0..3cb47c47 100644
--- a/src/syntax/ident.rs
+++ b/src/syntax/ident.rs
@@ -1,5 +1,3 @@
-//! Unicode identifiers.
-
use std::ops::Deref;
use unicode_xid::UnicodeXID;
@@ -44,7 +42,7 @@ impl Deref for Ident {
}
}
-/// Whether the string is a valid identifier.
+/// Whether a string is a valid identifier.
pub fn is_ident(string: &str) -> bool {
let mut chars = string.chars();
chars
@@ -52,12 +50,12 @@ pub fn is_ident(string: &str) -> bool {
.map_or(false, |c| is_id_start(c) && chars.all(is_id_continue))
}
-/// Whether the character can start an identifier.
+/// Whether a character can start an identifier.
pub fn is_id_start(c: char) -> bool {
c.is_xid_start() || c == '_'
}
-/// Whether the character can continue an identifier.
+/// Whether a character can continue an identifier.
pub fn is_id_continue(c: char) -> bool {
c.is_xid_continue() || c == '_' || c == '-'
}
diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs
index 970cd283..9c78fbc3 100644
--- a/src/syntax/mod.rs
+++ b/src/syntax/mod.rs
@@ -12,5 +12,5 @@ pub use node::*;
pub use span::*;
pub use token::*;
-/// A collection of nodes which form a tree together with the nodes' children.
-pub type SynTree = SpanVec<SynNode>;
+/// A collection of nodes which form a tree together with their children.
+pub type Tree = SpanVec<Node>;
diff --git a/src/syntax/node.rs b/src/syntax/node.rs
index cee810a2..d64d4fed 100644
--- a/src/syntax/node.rs
+++ b/src/syntax/node.rs
@@ -1,11 +1,8 @@
-//! Syntax tree nodes.
-
use super::*;
-/// A syntax node, which encompasses a single logical entity of parsed source
-/// code.
+/// A syntax node, encompassing a single logical entity of parsed source code.
#[derive(Debug, Clone, PartialEq)]
-pub enum SynNode {
+pub enum Node {
/// Plain text.
Text(String),
@@ -36,7 +33,7 @@ pub struct NodeHeading {
/// The section depth (numer of hashtags minus 1).
pub level: Spanned<u8>,
/// The contents of the heading.
- pub contents: SynTree,
+ pub contents: Tree,
}
/// A raw block with optional syntax highlighting: `` `raw` ``.
diff --git a/src/syntax/span.rs b/src/syntax/span.rs
index 20008354..3be770b8 100644
--- a/src/syntax/span.rs
+++ b/src/syntax/span.rs
@@ -1,5 +1,3 @@
-//! Mapping of values to the locations they originate from in source code.
-
use std::fmt::{self, Debug, Display, Formatter};
use std::ops::Range;
@@ -66,12 +64,18 @@ impl<T> Spanned<T> {
}
/// Map the value using a function while keeping the span.
- pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Spanned<U> {
+ pub fn map<F, U>(self, f: F) -> Spanned<U>
+ where
+ F: FnOnce(T) -> U,
+ {
Spanned { v: f(self.v), span: self.span }
}
/// Maps the span while keeping the value.
- pub fn map_span(mut self, f: impl FnOnce(Span) -> Span) -> Self {
+ pub fn map_span<F>(mut self, f: F) -> Self
+ where
+ F: FnOnce(Span) -> Span,
+ {
self.span = f(self.span);
self
}
@@ -102,7 +106,7 @@ impl<T: Debug> Debug for Spanned<T> {
}
}
-/// Locates a slice of source code.
+/// Bounds of a slice of source code.
#[derive(Copy, Clone, Ord, PartialOrd)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Span {
diff --git a/src/syntax/token.rs b/src/syntax/token.rs
index b365d8d3..ef17fac2 100644
--- a/src/syntax/token.rs
+++ b/src/syntax/token.rs
@@ -1,5 +1,3 @@
-//! Token definition.
-
use crate::geom::Unit;
/// A minimal semantic entity of source code.
diff --git a/tests/typ/example-coma.typ b/tests/typ/example-coma.typ
index d559a6fb..5374af1f 100644
--- a/tests/typ/example-coma.typ
+++ b/tests/typ/example-coma.typ
@@ -1,4 +1,4 @@
-// Test integration of syntax, page setup, box layout and alignment.
+// Test integration of syntax, library and layouting.
[page width: 450pt, height: 300pt, margins: 1cm]
diff --git a/tests/typeset.rs b/tests/typeset.rs
index 4f76d467..735a46c0 100644
--- a/tests/typeset.rs
+++ b/tests/typeset.rs
@@ -19,7 +19,7 @@ use typst::eval::State;
use typst::export::pdf;
use typst::font::FontLoader;
use typst::geom::{Length, Point, Sides, Size};
-use typst::layout::{BoxLayout, ImageElement, LayoutElement};
+use typst::layout::{Element, Frame, Image};
use typst::parse::{LineMap, Scanner};
use typst::shaping::Shaped;
use typst::syntax::{Location, Pos, SpanVec, Spanned, WithSpan};
@@ -134,16 +134,16 @@ fn test(
state.page.margins = Sides::uniform(Some(Length::pt(10.0).into()));
let Pass {
- output: layouts,
+ output: frames,
feedback: Feedback { mut diags, .. },
} = typeset(&src, Rc::clone(env), state);
diags.sort();
let env = env.borrow();
- let canvas = draw(&layouts, &env, 2.0);
+ let canvas = draw(&frames, &env, 2.0);
canvas.pixmap.save_png(png_path).unwrap();
- let pdf_data = pdf::export(&layouts, &env);
+ let pdf_data = pdf::export(&frames, &env);
fs::write(pdf_path, pdf_data).unwrap();
let mut ok = true;
@@ -226,12 +226,12 @@ fn print_diag(diag: &Spanned<Diag>, map: &LineMap) {
println!("{}: {}-{}: {}", diag.v.level, start, end, diag.v.message);
}
-fn draw(layouts: &[BoxLayout], env: &Env, pixel_per_pt: f32) -> Canvas {
+fn draw(frames: &[Frame], env: &Env, pixel_per_pt: f32) -> Canvas {
let pad = Length::pt(5.0);
- let height = pad + layouts.iter().map(|l| l.size.height + pad).sum::<Length>();
+ let height = pad + frames.iter().map(|l| l.size.height + pad).sum::<Length>();
let width = 2.0 * pad
- + layouts
+ + frames
.iter()
.map(|l| l.size.width)
.max_by(|a, b| a.partial_cmp(&b).unwrap())
@@ -244,7 +244,7 @@ fn draw(layouts: &[BoxLayout], env: &Env, pixel_per_pt: f32) -> Canvas {
canvas.pixmap.fill(Color::BLACK);
let mut origin = Point::new(pad, pad);
- for layout in layouts {
+ for frame in frames {
let mut paint = Paint::default();
paint.set_color(Color::WHITE);
@@ -252,26 +252,26 @@ fn draw(layouts: &[BoxLayout], env: &Env, pixel_per_pt: f32) -> Canvas {
Rect::from_xywh(
origin.x.to_pt() as f32,
origin.y.to_pt() as f32,
- layout.size.width.to_pt() as f32,
- layout.size.height.to_pt() as f32,
+ frame.size.width.to_pt() as f32,
+ frame.size.height.to_pt() as f32,
)
.unwrap(),
&paint,
);
- for &(pos, ref element) in &layout.elements {
+ for &(pos, ref element) in &frame.elements {
let pos = origin + pos;
match element {
- LayoutElement::Text(shaped) => {
+ Element::Text(shaped) => {
draw_text(&mut canvas, pos, env, shaped);
}
- LayoutElement::Image(image) => {
+ Element::Image(image) => {
draw_image(&mut canvas, pos, env, image);
}
}
}
- origin.y += layout.size.height + pad;
+ origin.y += frame.size.height + pad;
}
canvas
@@ -303,7 +303,7 @@ fn draw_text(canvas: &mut Canvas, pos: Point, env: &Env, shaped: &Shaped) {
}
}
-fn draw_image(canvas: &mut Canvas, pos: Point, env: &Env, element: &ImageElement) {
+fn draw_image(canvas: &mut Canvas, pos: Point, env: &Env, element: &Image) {
let img = &env.resources.loaded::<ImageResource>(element.res);
let mut pixmap = Pixmap::new(img.buf.width(), img.buf.height()).unwrap();