From 73dd5a085e1d877da6768b3fe67a4238863ba00b Mon Sep 17 00:00:00 2001 From: +merlan #flirora Date: Mon, 9 Dec 2024 04:33:30 -0500 Subject: [PATCH] Fix sizing of quadratic shapes (square/circle) (#5451) Co-authored-by: Laurenz Co-authored-by: PgBiel <9021226+PgBiel@users.noreply.github.com> --- crates/typst-layout/src/shapes.rs | 67 ++++++++++++++---- .../circle-beyond-page-width-overflows.png | Bin 0 -> 620 bytes tests/ref/circle-size-beyond-default.png | Bin 0 -> 1158 bytes tests/ref/rect-size-beyond-default.png | Bin 0 -> 185 bytes ...re-overflow.png => square-no-overflow.png} | Bin tests/ref/square-overflow-forced-height.png | Bin 0 -> 468 bytes tests/ref/square-overflow-forced-width.png | Bin 0 -> 457 bytes tests/ref/square-size-beyond-default.png | Bin 0 -> 198 bytes tests/suite/visualize/circle.typ | 12 ++++ tests/suite/visualize/rect.typ | 6 ++ tests/suite/visualize/square.typ | 27 ++++++- 11 files changed, 94 insertions(+), 18 deletions(-) create mode 100644 tests/ref/circle-beyond-page-width-overflows.png create mode 100644 tests/ref/circle-size-beyond-default.png create mode 100644 tests/ref/rect-size-beyond-default.png rename tests/ref/{square-overflow.png => square-no-overflow.png} (100%) create mode 100644 tests/ref/square-overflow-forced-height.png create mode 100644 tests/ref/square-overflow-forced-width.png create mode 100644 tests/ref/square-size-beyond-default.png diff --git a/crates/typst-layout/src/shapes.rs b/crates/typst-layout/src/shapes.rs index a35021721..db9acece3 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 { + 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 0000000000000000000000000000000000000000..941cb0092b24a4ea7548e69361bb6f4b0f9a4fc6 GIT binary patch literal 620 zcmV-y0+aoTP)%GI2?lHn00kCJ4m@}U3OsfM3Ov0J3aoB`0w0cp0^fFk0|!nx@NyCq z_;?f?xX0qai)*03=L6ut{sITq6mVd{=D>*^P+-dg4!pF=fo%aCcqKuCJ8wAfdJ-JC z_lg6b4ub>FZIj@xDhEE80tXIfIq>xeIIu30;Q1{Q+;_==E8XD0dov_h(Ma%6g#()+ zIItm+;N4jg97~blP>uw9OC;C@4qTof!9^blHY5_950YSACc&CQg40nFtj3)Nr=w20 zT)OPE2gHz^HYCcEBpB`*3GTn3EqhK|Z!-}RT=2sQpSC*-d>X*|w+_Vs0000~)y>-`na;?nq zk32d$ZxUP6S=}5R9sfIQeK}?JrW&t{qMULrN2E;p)0lZn-Sr}pzcDn)^1PH6n3J15 zxrZ-l1u7TT$A=Dg4j* z;ZBz=`|W4_$ugALINdTvR#xx#w=PERhacSPh4y#<_Er)**vHg=$ z?DxyuZp?GJcWlnD2Rn@MpRQ+ja0w8Y$kdUl>qY}a}IeU>Y|#Mj)n zWb48B?S~g!dVeb+f2VaSJ&NZb$;}pSyuC<_}JPn-~ZXqka(ztb9T#ecQ)tR+Jy%vN^(E^ zA?p5d-m)_P^ySOSe3;~9h=EHBkzyQlZPv74Pt-xFiF@7&d?EQ;F4gUe<)<;;Q;3PJB-1(f7fuh zJ!0|s63(?M!R2*8fob_dKDTY_629lJXc4rHd{DfPd#i!aHk~lt&8DnZ4bu2qH+n5R zJb$4aTh({F2FJ%w86CKL#M5PyPh5VnvDo+mD>G;M=dF=zUN~>q`D@>^=IfJBZ+m?A z@#$k>pI@$|lP#*Frv{Vh7MYA3ptK{NeXE`m#@03aEhbboFyt I=akR{03I?NnE(I) literal 0 HcmV?d00001 diff --git a/tests/ref/rect-size-beyond-default.png b/tests/ref/rect-size-beyond-default.png new file mode 100644 index 0000000000000000000000000000000000000000..1f4d80fe19b377ab56b2f06cce9e8e543971238e GIT binary patch literal 185 zcmeAS@N?(olHy`uVBq!ia0vp^6+pa%i5W;Leq#~^Qak}ZA+G=b|8Hn$sIRYgaByHu zoVFh*p5f`@7*cWT?KMTd1_K_qhqDwd4xX_PZi+g0uy8^O)7g&&zFy90&%W7RxB0)k zN#66U+Qp!=x`ATG6Hh+*w{yi!)mf`|&M*#}=%Y6IWXiv32USY1eb{c%U-7ee(#a?L kcCILH2`%wcTl`%+7@(Ksy;cUHx3vIVCg!0L4s85&!@I literal 0 HcmV?d00001 diff --git a/tests/ref/square-overflow.png b/tests/ref/square-no-overflow.png similarity index 100% rename from tests/ref/square-overflow.png rename to tests/ref/square-no-overflow.png diff --git a/tests/ref/square-overflow-forced-height.png b/tests/ref/square-overflow-forced-height.png new file mode 100644 index 0000000000000000000000000000000000000000..f7cb0ee351bc41d9b56d229cc9c7d2affb412cb4 GIT binary patch literal 468 zcmeAS@N?(olHy`uVBq!ia0vp^6+m3c0VEi%2k%` z*L7{h(rHo6(^?iNDrqcQxWcrf?U3As!>f61rzr$`p6&2%oaC6qH7P+sG*MM4aEnO? z3+L2IW9{|(542=_ZIsymzHHg~vU3Hi0+y}f5LWv$KcSRSlxel7(>=`=rv%2z zJ+|=5Q~@{LhItAM2NoZCAX{{{NE3$3Zcd7k9P_;RXZPMIR+JVYEDv8@8 zFC|Meb58dS582@+a`f4TE3aM`r9C_>nlYX6Or>JqiOcE_?cy)*P5knAGEMLu7GH36#({L(U?B^_e<{;G`4A>&QdV$XBB qiN)76IajIQSe|ny21{`K|Hf literal 0 HcmV?d00001 diff --git a/tests/ref/square-overflow-forced-width.png b/tests/ref/square-overflow-forced-width.png new file mode 100644 index 0000000000000000000000000000000000000000..4667181687ecf6ee0f9081d52fc7e6404e28acd4 GIT binary patch literal 457 zcmeAS@N?(olHy`uVBq!ia0vp^Hb8um14uALo#10(U|^i!>EaktaqI0(TW_aCiDQNK zuGv?Dyw^Hx^z`!laH#YG9RlKTc@T+lVeEB)DD-6mlT9l zT%MiOv59NsY~xdEa*6%_va;=dQNHvc&$@MwSU>CVe)&m0#_`k^FHzW>R-my>TRRctTg|ECtp#US=P>1_FlZ<5Vk z;Vg^U9Lg0we_3Hz(4S*0*U^@=!*9}(bpm^?nf{c1eKtzcqh!T8*WL^B8}6og_U3&# z9%~lMxK7M>7ifJ@#9FMsXL|Hr%^V7_ahY z;z90c3+XdSwqmlsetKBsZaQEn!ukAhh-kyN&)0WF7*!cG&U+BppJT!-Fem?5=!)EM zFUwgm(Z`dWolYIUHnH}{@vxwEALo9#^yTRe&UaVenlwCk(d#yOM(m+yJ;6d(&TsI) nwS!Mc7zREoIQW1-OywWer4uzJAMd+j2#O<5S3j3^P6P_V&($L*m)mX)KgwXoW8Ee|)xc2*ro1Y(HSYbJ$>a>)k_njX