diff options
| author | Laurenz <laurmaedje@gmail.com> | 2023-07-02 19:59:52 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2023-07-02 20:07:43 +0200 |
| commit | ebfdb1dafa430786db10dad2ef7d5467c1bdbed1 (patch) | |
| tree | 2bbc24ddb4124c4bb14dec0e536129d4de37b056 /crates/typst-library/src/layout/pad.rs | |
| parent | 3ab19185093d7709f824b95b979060ce125389d8 (diff) | |
Move everything into `crates/` directory
Diffstat (limited to 'crates/typst-library/src/layout/pad.rs')
| -rw-r--r-- | crates/typst-library/src/layout/pad.rs | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/crates/typst-library/src/layout/pad.rs b/crates/typst-library/src/layout/pad.rs new file mode 100644 index 00000000..a3d5646b --- /dev/null +++ b/crates/typst-library/src/layout/pad.rs @@ -0,0 +1,125 @@ +use crate::prelude::*; + +/// Adds spacing around content. +/// +/// The spacing can be specified for each side individually, or for all sides at +/// once by specifying a positional argument. +/// +/// ## Example { #example } +/// ```example +/// #set align(center) +/// +/// #pad(x: 16pt, image("typing.jpg")) +/// _Typing speeds can be +/// measured in words per minute._ +/// ``` +/// +/// Display: Padding +/// Category: layout +#[element(Layout)] +pub struct PadElem { + /// The padding at the left side. + #[parse( + let all = args.named("rest")?.or(args.find()?); + let x = args.named("x")?.or(all); + let y = args.named("y")?.or(all); + args.named("left")?.or(x) + )] + pub left: Rel<Length>, + + /// The padding at the top side. + #[parse(args.named("top")?.or(y))] + pub top: Rel<Length>, + + /// The padding at the right side. + #[parse(args.named("right")?.or(x))] + pub right: Rel<Length>, + + /// The padding at the bottom side. + #[parse(args.named("bottom")?.or(y))] + pub bottom: Rel<Length>, + + /// The horizontal padding. Both `left` and `right` take precedence over + /// this. + #[external] + pub x: Rel<Length>, + + /// The vertical padding. Both `top` and `bottom` take precedence over this. + #[external] + pub y: Rel<Length>, + + /// The padding for all sides. All other parameters take precedence over + /// this. + #[external] + pub rest: Rel<Length>, + + /// The content to pad at the sides. + #[required] + pub body: Content, +} + +impl Layout for PadElem { + #[tracing::instrument(name = "PadElem::layout", skip_all)] + fn layout( + &self, + vt: &mut Vt, + styles: StyleChain, + regions: Regions, + ) -> SourceResult<Fragment> { + let sides = Sides::new( + self.left(styles), + self.top(styles), + self.right(styles), + self.bottom(styles), + ); + + // Layout child into padded regions. + let mut backlog = vec![]; + let padding = sides.resolve(styles); + let pod = regions.map(&mut backlog, |size| shrink(size, padding)); + let mut fragment = self.body().layout(vt, styles, pod)?; + + for frame in &mut fragment { + // Apply the padding inversely such that the grown size padded + // yields the frame's size. + let padded = grow(frame.size(), padding); + let padding = padding.relative_to(padded); + let offset = Point::new(padding.left, padding.top); + + // Grow the frame and translate everything in the frame inwards. + frame.set_size(padded); + frame.translate(offset); + } + + Ok(fragment) + } +} + +/// Shrink a size by padding relative to the size itself. +fn shrink(size: Size, padding: Sides<Rel<Abs>>) -> Size { + size - padding.relative_to(size).sum_by_axis() +} + +/// Grow a size by padding 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 padding, +/// r be the right padding, +/// 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) +fn grow(size: Size, padding: Sides<Rel<Abs>>) -> Size { + size.zip(padding.sum_by_axis()) + .map(|(s, p)| (s + p.abs).safe_div(1.0 - p.rel.get())) +} |
