summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-03-10 17:42:47 +0100
committerLaurenz <laurmaedje@gmail.com>2021-03-10 17:42:47 +0100
commit4e5f85aa4ac0d6b51323bb2a6e1fbd3f4f46babb (patch)
tree87f157c2058800e510e5538b0815c947c14fe22d
parentb0446cbdd189a8cc3b6a7321579f4aa89415a8e0 (diff)
Pad function 🔲
-rw-r--r--src/exec/context.rs116
-rw-r--r--src/library/mod.rs3
-rw-r--r--src/library/pad.rs38
-rw-r--r--src/library/shapes.rs22
-rw-r--r--tests/ref/library/pad.pngbin0 -> 180 bytes
-rw-r--r--tests/typ/library/pad.typ3
6 files changed, 107 insertions, 75 deletions
diff --git a/src/exec/context.rs b/src/exec/context.rs
index b37b15cb..d491a251 100644
--- a/src/exec/context.rs
+++ b/src/exec/context.rs
@@ -80,6 +80,45 @@ impl<'a> ExecContext<'a> {
self.inner.push(node);
}
+ /// Push a normal word space.
+ pub fn push_space(&mut self) {
+ let em = self.state.font.font_size();
+ self.push(NodeSpacing {
+ amount: self.state.par.word_spacing.resolve(em),
+ softness: Softness::Soft,
+ });
+ }
+
+ /// Push text into the context.
+ ///
+ /// The text is split into lines at newlines.
+ pub fn push_text(&mut self, text: &str) {
+ let mut newline = false;
+ for line in text.split_terminator(is_newline) {
+ if newline {
+ self.apply_linebreak();
+ }
+
+ let node = self.make_text_node(line.into());
+ self.push(node);
+ newline = true;
+ }
+ }
+
+ /// Execute the body of a function and return the result as a stack node.
+ pub fn exec_body(&mut self, body: &ValueTemplate, expand: Spec<Expansion>) -> Node {
+ let dirs = self.state.dirs;
+ let align = self.state.align;
+
+ self.start_group(ContentGroup);
+ self.start_par_group();
+ body.exec(self);
+ self.end_par_group();
+ let children = self.end_group::<ContentGroup>().1;
+
+ NodeStack { dirs, align, expand, children }.into()
+ }
+
/// Start a page group based on the active page state.
///
/// The `softness` is a hint on whether empty pages should be kept in the
@@ -130,22 +169,6 @@ impl<'a> ExecContext<'a> {
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();
@@ -218,29 +241,28 @@ impl<'a> ExecContext<'a> {
}
}
- /// Push a normal word space.
- pub fn push_space(&mut self) {
+ /// Set the font to monospace.
+ pub fn apply_monospace(&mut self) {
+ let families = self.state.font.families_mut();
+ families.list.insert(0, "monospace".to_string());
+ families.flatten();
+ }
+
+ /// 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.word_spacing.resolve(em),
+ amount: self.state.par.par_spacing.resolve(em),
softness: Softness::Soft,
});
- }
-
- /// Push text into the context.
- ///
- /// The text is split into lines at newlines.
- pub fn push_text(&mut self, text: &str) {
- let mut newline = false;
- for line in text.split_terminator(is_newline) {
- if newline {
- self.apply_linebreak();
- }
-
- let node = self.make_text_node(line.into());
- self.push(node);
- newline = true;
- }
+ self.start_par_group();
}
/// Construct a text node from the given string based on the active text
@@ -269,30 +291,6 @@ impl<'a> ExecContext<'a> {
variant,
}
}
-
- /// Set the font to monospace.
- pub fn apply_monospace(&mut self) {
- let families = self.state.font.families_mut();
- families.list.insert(0, "monospace".to_string());
- families.flatten();
- }
-
- /// 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();
- }
}
/// Defines how an item interacts with surrounding items.
diff --git a/src/library/mod.rs b/src/library/mod.rs
index f846e6ee..54d6a14c 100644
--- a/src/library/mod.rs
+++ b/src/library/mod.rs
@@ -7,6 +7,7 @@ mod align;
mod base;
mod font;
mod image;
+mod pad;
mod page;
mod shapes;
mod spacing;
@@ -15,6 +16,7 @@ pub use self::image::*;
pub use align::*;
pub use base::*;
pub use font::*;
+pub use pad::*;
pub use page::*;
pub use shapes::*;
pub use spacing::*;
@@ -46,6 +48,7 @@ pub fn new() -> Scope {
set!(func: "font", font);
set!(func: "h", h);
set!(func: "image", image);
+ set!(func: "pad", pad);
set!(func: "page", page);
set!(func: "pagebreak", pagebreak);
set!(func: "repr", repr);
diff --git a/src/library/pad.rs b/src/library/pad.rs
new file mode 100644
index 00000000..f9e4386d
--- /dev/null
+++ b/src/library/pad.rs
@@ -0,0 +1,38 @@
+use super::*;
+
+/// `pad`: Pad content at the sides.
+///
+/// # Positional arguments
+/// - Padding for all sides: `padding`, of type `linear` relative to sides.
+/// - Body: of type `template`.
+///
+/// # Named arguments
+/// - Left padding: `left`, of type `linear` relative to parent width.
+/// - Right padding: `right`, of type `linear` relative to parent width.
+/// - Top padding: `top`, of type `linear` relative to parent height.
+/// - Bottom padding: `bottom`, of type `linear` relative to parent height.
+pub fn pad(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value {
+ let all = args.find(ctx);
+ let left = args.get(ctx, "left");
+ let top = args.get(ctx, "top");
+ let right = args.get(ctx, "right");
+ let bottom = args.get(ctx, "bottom");
+ let body = args.require::<ValueTemplate>(ctx, "body").unwrap_or_default();
+
+ let padding = Sides::new(
+ left.or(all).unwrap_or_default(),
+ top.or(all).unwrap_or_default(),
+ right.or(all).unwrap_or_default(),
+ bottom.or(all).unwrap_or_default(),
+ );
+
+ Value::template("pad", move |ctx| {
+ let snapshot = ctx.state.clone();
+
+ let expand = Spec::uniform(Expansion::Fit);
+ let child = ctx.exec_body(&body, expand);
+ ctx.push(NodePad { padding, child });
+
+ ctx.state = snapshot;
+ })
+}
diff --git a/src/library/shapes.rs b/src/library/shapes.rs
index f9685fce..5e638c01 100644
--- a/src/library/shapes.rs
+++ b/src/library/shapes.rs
@@ -24,28 +24,18 @@ pub fn box_(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value {
let main = args.get(ctx, "main-dir");
let cross = args.get(ctx, "cross-dir");
let color = args.get(ctx, "color");
- let body = args.find::<ValueTemplate>(ctx);
+ let body = args.find::<ValueTemplate>(ctx).unwrap_or_default();
+
+ let fill_if = |c| if c { Expansion::Fill } else { Expansion::Fit };
+ let expand = Spec::new(fill_if(width.is_some()), fill_if(height.is_some()));
Value::template("box", move |ctx| {
let snapshot = ctx.state.clone();
ctx.set_dirs(Gen::new(main, cross));
- let dirs = ctx.state.dirs;
- let align = ctx.state.align;
-
- ctx.start_content_group();
- if let Some(body) = &body {
- body.exec(ctx);
- }
- let children = ctx.end_content_group();
- let fill_if = |c| if c { Expansion::Fill } else { Expansion::Fit };
- let expand = Spec::new(fill_if(width.is_some()), fill_if(height.is_some()));
- let fixed = NodeFixed {
- width,
- height,
- child: NodeStack { dirs, align, expand, children }.into(),
- };
+ let child = ctx.exec_body(&body, expand);
+ let fixed = NodeFixed { width, height, child };
if let Some(color) = color {
ctx.push(NodeBackground {
diff --git a/tests/ref/library/pad.png b/tests/ref/library/pad.png
new file mode 100644
index 00000000..46380689
--- /dev/null
+++ b/tests/ref/library/pad.png
Binary files differ
diff --git a/tests/typ/library/pad.typ b/tests/typ/library/pad.typ
new file mode 100644
index 00000000..3ee538c2
--- /dev/null
+++ b/tests/typ/library/pad.typ
@@ -0,0 +1,3 @@
+#box(color: #9feb52)[
+ #pad(10pt, box(color: #eb5278, width: 20pt, height: 20pt))
+]