summaryrefslogtreecommitdiff
path: root/crates/typst-layout/src/inline/box.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/inline/box.rs
parentb8034a343831e8609aec2ec81eb7eeda57aa5d81 (diff)
Split out four new crates (#5302)
Diffstat (limited to 'crates/typst-layout/src/inline/box.rs')
-rw-r--r--crates/typst-layout/src/inline/box.rs87
1 files changed, 87 insertions, 0 deletions
diff --git a/crates/typst-layout/src/inline/box.rs b/crates/typst-layout/src/inline/box.rs
new file mode 100644
index 00000000..30572e4e
--- /dev/null
+++ b/crates/typst-layout/src/inline/box.rs
@@ -0,0 +1,87 @@
+use once_cell::unsync::Lazy;
+use typst_library::diag::SourceResult;
+use typst_library::engine::Engine;
+use typst_library::foundations::{Packed, StyleChain};
+use typst_library::introspection::Locator;
+use typst_library::layout::{BoxElem, Frame, FrameKind, Size};
+use typst_library::visualize::Stroke;
+use typst_utils::Numeric;
+
+use crate::flow::unbreakable_pod;
+use crate::shapes::{clip_rect, fill_and_stroke};
+
+/// Lay out a box as part of a paragraph.
+#[typst_macros::time(name = "box", span = elem.span())]
+pub fn layout_box(
+ elem: &Packed<BoxElem>,
+ engine: &mut Engine,
+ locator: Locator,
+ styles: StyleChain,
+ region: Size,
+) -> SourceResult<Frame> {
+ // Fetch sizing properties.
+ let width = elem.width(styles);
+ let height = elem.height(styles);
+ let inset = elem.inset(styles).unwrap_or_default();
+
+ // Build the pod region.
+ let pod = unbreakable_pod(&width, &height.into(), &inset, styles, region);
+
+ // Layout the body.
+ let mut frame = match elem.body(styles) {
+ // If we have no body, just create an empty frame. If necessary,
+ // its size will be adjusted below.
+ None => Frame::hard(Size::zero()),
+
+ // If we have a child, layout it into the body. Boxes are boundaries
+ // for gradient relativeness, so we set the `FrameKind` to `Hard`.
+ Some(body) => crate::layout_frame(engine, body, locator, styles, pod)?
+ .with_kind(FrameKind::Hard),
+ };
+
+ // Enforce a correct frame size on the expanded axes. Do this before
+ // applying the inset, since the pod shrunk.
+ frame.set_size(pod.expand.select(pod.size, frame.size()));
+
+ // Apply the inset.
+ if !inset.is_zero() {
+ crate::pad::grow(&mut frame, &inset);
+ }
+
+ // Prepare fill and stroke.
+ let fill = elem.fill(styles);
+ let stroke = elem
+ .stroke(styles)
+ .unwrap_or_default()
+ .map(|s| s.map(Stroke::unwrap_or_default));
+
+ // Only fetch these if necessary (for clipping or filling/stroking).
+ let outset = Lazy::new(|| elem.outset(styles).unwrap_or_default());
+ let radius = Lazy::new(|| elem.radius(styles).unwrap_or_default());
+
+ // Clip the contents, if requested.
+ if elem.clip(styles) {
+ let size = frame.size() + outset.relative_to(frame.size()).sum_by_axis();
+ frame.clip(clip_rect(size, &radius, &stroke));
+ }
+
+ // Add fill and/or stroke.
+ if fill.is_some() || stroke.iter().any(Option::is_some) {
+ fill_and_stroke(&mut frame, fill, &stroke, &outset, &radius, elem.span());
+ }
+
+ // Assign label to the frame.
+ if let Some(label) = elem.label() {
+ frame.label(label);
+ }
+
+ // Apply baseline shift. Do this after setting the size and applying the
+ // inset, so that a relative shift is resolved relative to the final
+ // height.
+ let shift = elem.baseline(styles).relative_to(frame.height());
+ if !shift.is_zero() {
+ frame.set_baseline(frame.baseline() - shift);
+ }
+
+ Ok(frame)
+}