summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author+merlan #flirora <uruwi@protonmail.com>2024-12-09 04:33:30 -0500
committerGitHub <noreply@github.com>2024-12-09 09:33:30 +0000
commit73dd5a085e1d877da6768b3fe67a4238863ba00b (patch)
tree8ee71727f7878cf786748c93cdb44002f842234a
parent4729d3d3bdf52268d143b9fe0ba6b097eae32bf8 (diff)
Fix sizing of quadratic shapes (square/circle) (#5451)
Co-authored-by: Laurenz <laurmaedje@gmail.com> Co-authored-by: PgBiel <9021226+PgBiel@users.noreply.github.com>
-rw-r--r--crates/typst-layout/src/shapes.rs67
-rw-r--r--tests/ref/circle-beyond-page-width-overflows.pngbin0 -> 620 bytes
-rw-r--r--tests/ref/circle-size-beyond-default.pngbin0 -> 1158 bytes
-rw-r--r--tests/ref/rect-size-beyond-default.pngbin0 -> 185 bytes
-rw-r--r--tests/ref/square-no-overflow.png (renamed from tests/ref/square-overflow.png)bin290 -> 290 bytes
-rw-r--r--tests/ref/square-overflow-forced-height.pngbin0 -> 468 bytes
-rw-r--r--tests/ref/square-overflow-forced-width.pngbin0 -> 457 bytes
-rw-r--r--tests/ref/square-size-beyond-default.pngbin0 -> 198 bytes
-rw-r--r--tests/suite/visualize/circle.typ12
-rw-r--r--tests/suite/visualize/rect.typ6
-rw-r--r--tests/suite/visualize/square.typ27
11 files changed, 94 insertions, 18 deletions
diff --git a/crates/typst-layout/src/shapes.rs b/crates/typst-layout/src/shapes.rs
index a3502172..db9acece 100644
--- a/crates/typst-layout/src/shapes.rs
+++ b/crates/typst-layout/src/shapes.rs
@@ -348,29 +348,48 @@ fn layout_shape(
pod.size = crate::pad::shrink(region.size, &inset);
}
- // Layout the child.
- frame = crate::layout_frame(engine, child, locator.relayout(), styles, pod)?;
-
- // If the child is a square or circle, relayout with full expansion into
- // square region to make sure the result is really quadratic.
+ // If the shape is quadratic, we first measure it to determine its size
+ // and then layout with full expansion to force the aspect ratio and
+ // make sure it's really quadratic.
if kind.is_quadratic() {
- let length = frame.size().max_by_side().min(pod.size.min_by_side());
- let quad_pod = Region::new(Size::splat(length), Axes::splat(true));
- frame = crate::layout_frame(engine, child, locator, styles, quad_pod)?;
+ let length = match quadratic_size(pod) {
+ Some(length) => length,
+ None => {
+ // Take as much as the child wants, but without overflowing.
+ crate::layout_frame(engine, child, locator.relayout(), styles, pod)?
+ .size()
+ .max_by_side()
+ .min(pod.size.min_by_side())
+ }
+ };
+
+ pod = Region::new(Size::splat(length), Axes::splat(true));
}
+ // Layout the child.
+ frame = crate::layout_frame(engine, child, locator, styles, pod)?;
+
// Apply the inset.
if has_inset {
crate::pad::grow(&mut frame, &inset);
}
} else {
- // The default size that a shape takes on if it has no child and
- // enough space.
- let default = Size::new(Abs::pt(45.0), Abs::pt(30.0));
- let mut size = region.expand.select(region.size, default.min(region.size));
- if kind.is_quadratic() {
- size = Size::splat(size.min_by_side());
- }
+ // The default size that a shape takes on if it has no child and no
+ // forced sizes.
+ let default = Size::new(Abs::pt(45.0), Abs::pt(30.0)).min(region.size);
+
+ let size = if kind.is_quadratic() {
+ Size::splat(match quadratic_size(region) {
+ Some(length) => length,
+ None => default.min_by_side(),
+ })
+ } else {
+ // For each dimension, pick the region size if forced, otherwise
+ // use the default size (or the region size if the default
+ // is too large for the region).
+ region.expand.select(region.size, default)
+ };
+
frame = Frame::soft(size);
}
@@ -411,6 +430,24 @@ fn layout_shape(
Ok(frame)
}
+/// Determines the forced size of a quadratic shape based on the region, if any.
+///
+/// The size is forced if at least one axis is expanded because `expand` is
+/// `true` for axes whose size was manually specified by the user.
+fn quadratic_size(region: Region) -> Option<Abs> {
+ if region.expand.x && region.expand.y {
+ // If both `width` and `height` are specified, we choose the
+ // smaller one.
+ Some(region.size.x.min(region.size.y))
+ } else if region.expand.x {
+ Some(region.size.x)
+ } else if region.expand.y {
+ Some(region.size.y)
+ } else {
+ None
+ }
+}
+
/// Creates a new rectangle as a path.
pub fn clip_rect(
size: Size,
diff --git a/tests/ref/circle-beyond-page-width-overflows.png b/tests/ref/circle-beyond-page-width-overflows.png
new file mode 100644
index 00000000..941cb009
--- /dev/null
+++ b/tests/ref/circle-beyond-page-width-overflows.png
Binary files differ
diff --git a/tests/ref/circle-size-beyond-default.png b/tests/ref/circle-size-beyond-default.png
new file mode 100644
index 00000000..f25aad07
--- /dev/null
+++ b/tests/ref/circle-size-beyond-default.png
Binary files differ
diff --git a/tests/ref/rect-size-beyond-default.png b/tests/ref/rect-size-beyond-default.png
new file mode 100644
index 00000000..1f4d80fe
--- /dev/null
+++ b/tests/ref/rect-size-beyond-default.png
Binary files differ
diff --git a/tests/ref/square-overflow.png b/tests/ref/square-no-overflow.png
index 81024ee6..81024ee6 100644
--- a/tests/ref/square-overflow.png
+++ b/tests/ref/square-no-overflow.png
Binary files differ
diff --git a/tests/ref/square-overflow-forced-height.png b/tests/ref/square-overflow-forced-height.png
new file mode 100644
index 00000000..f7cb0ee3
--- /dev/null
+++ b/tests/ref/square-overflow-forced-height.png
Binary files differ
diff --git a/tests/ref/square-overflow-forced-width.png b/tests/ref/square-overflow-forced-width.png
new file mode 100644
index 00000000..46671816
--- /dev/null
+++ b/tests/ref/square-overflow-forced-width.png
Binary files differ
diff --git a/tests/ref/square-size-beyond-default.png b/tests/ref/square-size-beyond-default.png
new file mode 100644
index 00000000..a513a7db
--- /dev/null
+++ b/tests/ref/square-size-beyond-default.png
Binary files differ
diff --git a/tests/suite/visualize/circle.typ b/tests/suite/visualize/circle.typ
index 43459eb5..0687e068 100644
--- a/tests/suite/visualize/circle.typ
+++ b/tests/suite/visualize/circle.typ
@@ -67,3 +67,15 @@ Expanded by height.
circle(width: 10%),
circle(height: 50%),
)
+
+--- circle-size-beyond-default ---
+// Test that setting a circle's height beyond its default sizes it correctly.
+#circle()
+#circle(height: 60pt)
+#circle(width: 60pt)
+#circle(radius: 30pt)
+
+--- circle-beyond-page-width-overflows ---
+// Test that sizing a circle beyond the page width correctly overflows the page.
+#set page(height: 100pt)
+#circle(width: 150%)
diff --git a/tests/suite/visualize/rect.typ b/tests/suite/visualize/rect.typ
index f84fafcb..5dfe29f3 100644
--- a/tests/suite/visualize/rect.typ
+++ b/tests/suite/visualize/rect.typ
@@ -105,3 +105,9 @@
#align(right, rect(width: -1cm, fill: gradient.linear(red, blue))[Reverse right])
#align(right, rect(width: 1cm, fill: gradient.linear(red, blue))[Right])
+
+--- rect-size-beyond-default ---
+// Test that setting a rectangle's height beyond its default sizes it correctly.
+#rect()
+#rect(height: 60pt)
+#rect(width: 60pt)
diff --git a/tests/suite/visualize/square.typ b/tests/suite/visualize/square.typ
index b346561d..bb7bbd9d 100644
--- a/tests/suite/visualize/square.typ
+++ b/tests/suite/visualize/square.typ
@@ -77,11 +77,25 @@
#set page(width: 20pt, height: 10pt, margin: 0pt)
#stack(dir: ltr, square(fill: forest), square(fill: conifer))
---- square-overflow ---
+--- square-no-overflow ---
// Test that square doesn't overflow due to its aspect ratio.
#set page(width: 40pt, height: 25pt, margin: 5pt)
-#square(width: 100%)
-#square(width: 100%)[Hello there]
+#square()
+#square[Hello there]
+
+--- square-overflow-forced-width ---
+// Test that a width-overflowing square is laid out regardless of the
+// presence of inner content.
+#set page(width: 60pt, height: 100pt)
+#square(width: 150%)
+#square(width: 150%)[Hello there]
+
+--- square-overflow-forced-height ---
+// Test that a height-overflowing square is laid out regardless of the
+// presence of inner content.
+#set page(width: 120pt, height: 60pt)
+#square(height: 150%)
+#square(height: 150%)[Hello there]
--- square-size-relative-invalid ---
// Size cannot be relative because we wouldn't know
@@ -144,3 +158,10 @@
// Test that square sets correct base for its content.
#set page(height: 80pt)
#square(width: 40%, rect(width: 60%, height: 80%))
+
+--- square-size-beyond-default ---
+// Test that setting a square's height beyond its default sizes it correctly.
+#square()
+#square(height: 60pt)
+#square(width: 60pt)
+#square(size: 60pt)