summaryrefslogtreecommitdiff
path: root/crates/typst-layout/src/pad.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2024-10-27 19:04:55 +0100
committerGitHub <noreply@github.com>2024-10-27 18:04:55 +0000
commitbe7cfc85d08c545abfac08098b7b33b4bd71f37e (patch)
treef4137fa2aaa57babae1f7603a9b2ed7e688f43d8 /crates/typst-layout/src/pad.rs
parentb8034a343831e8609aec2ec81eb7eeda57aa5d81 (diff)
Split out four new crates (#5302)
Diffstat (limited to 'crates/typst-layout/src/pad.rs')
-rw-r--r--crates/typst-layout/src/pad.rs93
1 files changed, 93 insertions, 0 deletions
diff --git a/crates/typst-layout/src/pad.rs b/crates/typst-layout/src/pad.rs
new file mode 100644
index 00000000..00badcdb
--- /dev/null
+++ b/crates/typst-layout/src/pad.rs
@@ -0,0 +1,93 @@
+use typst_library::diag::SourceResult;
+use typst_library::engine::Engine;
+use typst_library::foundations::{Packed, Resolve, StyleChain};
+use typst_library::introspection::Locator;
+use typst_library::layout::{
+ Abs, Fragment, Frame, PadElem, Point, Regions, Rel, Sides, Size,
+};
+
+/// Layout the padded content.
+#[typst_macros::time(span = elem.span())]
+pub fn layout_pad(
+ elem: &Packed<PadElem>,
+ engine: &mut Engine,
+ locator: Locator,
+ styles: StyleChain,
+ regions: Regions,
+) -> SourceResult<Fragment> {
+ let padding = Sides::new(
+ elem.left(styles).resolve(styles),
+ elem.top(styles).resolve(styles),
+ elem.right(styles).resolve(styles),
+ elem.bottom(styles).resolve(styles),
+ );
+
+ let mut backlog = vec![];
+ let pod = regions.map(&mut backlog, |size| shrink(size, &padding));
+
+ // Layout child into padded regions.
+ let mut fragment = crate::layout_fragment(engine, &elem.body, locator, styles, pod)?;
+
+ for frame in &mut fragment {
+ grow(frame, &padding);
+ }
+
+ Ok(fragment)
+}
+
+/// Shrink a region size by an inset relative to the size itself.
+pub fn shrink(size: Size, inset: &Sides<Rel<Abs>>) -> Size {
+ size - inset.sum_by_axis().relative_to(size)
+}
+
+/// Shrink the components of possibly multiple `Regions` by an inset relative to
+/// the regions themselves.
+pub fn shrink_multiple(
+ size: &mut Size,
+ full: &mut Abs,
+ backlog: &mut [Abs],
+ last: &mut Option<Abs>,
+ inset: &Sides<Rel<Abs>>,
+) {
+ let summed = inset.sum_by_axis();
+ *size -= summed.relative_to(*size);
+ *full -= summed.y.relative_to(*full);
+ for item in backlog {
+ *item -= summed.y.relative_to(*item);
+ }
+ *last = last.map(|v| v - summed.y.relative_to(v));
+}
+
+/// Grow a frame's size by an inset relative to the grown size.
+/// This is the inverse operation to `shrink()`.
+///
+/// For the horizontal axis the derivation looks as follows.
+/// (Vertical axis is analogous.)
+///
+/// Let w be the grown target width,
+/// s be the given width,
+/// l be the left inset,
+/// r be the right inset,
+/// p = l + r.
+///
+/// We want that: w - l.resolve(w) - r.resolve(w) = s
+///
+/// Thus: w - l.resolve(w) - r.resolve(w) = s
+/// <=> w - p.resolve(w) = s
+/// <=> w - p.rel * w - p.abs = s
+/// <=> (1 - p.rel) * w = s + p.abs
+/// <=> w = (s + p.abs) / (1 - p.rel)
+pub fn grow(frame: &mut Frame, inset: &Sides<Rel<Abs>>) {
+ // Apply the padding inversely such that the grown size padded
+ // yields the frame's size.
+ let padded = frame
+ .size()
+ .zip_map(inset.sum_by_axis(), |s, p| (s + p.abs) / (1.0 - p.rel.get()));
+
+ let inset = inset.relative_to(padded);
+ let offset = Point::new(inset.left, inset.top);
+
+ // Grow the frame and translate everything in the frame inwards.
+ frame.set_size(padded);
+ frame.translate(offset);
+}