From 3aa18beacf84e8e982a1cb28170d281769c06dd0 Mon Sep 17 00:00:00 2001 From: shinyfelix <101457412+shinyfelix@users.noreply.github.com> Date: Sat, 20 Jul 2024 14:26:57 +0200 Subject: [PATCH] Fix approximated size of `reflow: true` transformations (#4462) Co-authored-by: Laurenz --- crates/typst/src/layout/transform.rs | 33 ++++++++++-------- tests/ref/transform-scale-relative-sizing.png | Bin 1975 -> 2033 bytes tests/ref/transform-scale.png | Bin 1802 -> 1804 bytes 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/crates/typst/src/layout/transform.rs b/crates/typst/src/layout/transform.rs index ad3668d10..dcb178fdb 100644 --- a/crates/typst/src/layout/transform.rs +++ b/crates/typst/src/layout/transform.rs @@ -12,6 +12,7 @@ use crate::layout::{ Abs, Alignment, Angle, Axes, BlockElem, FixedAlignment, Frame, HAlignment, Length, Point, Ratio, Region, Regions, Rel, Size, VAlignment, }; +use crate::utils::Numeric; /// Moves content without affecting layout. /// @@ -153,12 +154,11 @@ fn layout_rotate( let align = elem.origin(styles).resolve(styles); // Compute the new region's approximate size. - let size = region - .size - .to_point() - .transform_inf(Transform::rotate(angle)) - .map(Abs::abs) - .to_size(); + let size = if region.size.is_finite() { + compute_bounding_box(region.size, Transform::rotate(-angle)).1 + } else { + Size::splat(Abs::inf()) + }; measure_and_layout( engine, @@ -248,7 +248,10 @@ fn layout_scale( ) -> SourceResult { // Compute the new region's approximate size. let scale = elem.resolve_scale(engine, locator.relayout(), region.size, styles)?; - let size = region.size.zip_map(scale, |r, s| s.of(r)).map(Abs::abs); + let size = region + .size + .zip_map(scale, |r, s| if r.is_finite() { Ratio::new(1.0 / s).of(r) } else { r }) + .map(Abs::abs); measure_and_layout( engine, @@ -489,7 +492,7 @@ fn measure_and_layout( .pre_concat(Transform::translate(-x, -y)); // Compute the bounding box and offset and wrap in a new frame. - let (offset, size) = compute_bounding_box(&frame, ts); + let (offset, size) = compute_bounding_box(frame.size(), ts); frame.transform(ts); frame.translate(offset); frame.set_size(size); @@ -512,20 +515,20 @@ fn measure_and_layout( } } -/// Computes the bounding box and offset of a transformed frame. -fn compute_bounding_box(frame: &Frame, ts: Transform) -> (Point, Size) { +/// Computes the bounding box and offset of a transformed area. +fn compute_bounding_box(size: Size, ts: Transform) -> (Point, Size) { let top_left = Point::zero().transform_inf(ts); - let top_right = Point::new(frame.width(), Abs::zero()).transform_inf(ts); - let bottom_left = Point::new(Abs::zero(), frame.height()).transform_inf(ts); - let bottom_right = Point::new(frame.width(), frame.height()).transform_inf(ts); + let top_right = Point::with_x(size.x).transform_inf(ts); + let bottom_left = Point::with_y(size.y).transform_inf(ts); + let bottom_right = size.to_point().transform_inf(ts); - // We first compute the new bounding box of the rotated frame. + // We first compute the new bounding box of the rotated area. let min_x = top_left.x.min(top_right.x).min(bottom_left.x).min(bottom_right.x); let min_y = top_left.y.min(top_right.y).min(bottom_left.y).min(bottom_right.y); let max_x = top_left.x.max(top_right.x).max(bottom_left.x).max(bottom_right.x); let max_y = top_left.y.max(top_right.y).max(bottom_left.y).max(bottom_right.y); - // Then we compute the new size of the frame. + // Then we compute the new size of the area. let width = max_x - min_x; let height = max_y - min_y; diff --git a/tests/ref/transform-scale-relative-sizing.png b/tests/ref/transform-scale-relative-sizing.png index d10bd3ff4f364915a2884bd2991479fc1b9b9d45..a6c18a98e9c6a9ed3747ca9d1ec150de126d3208 100644 GIT binary patch delta 1882 zcmV-g2c`J85AhF>B!9R`L_t(|+U?tYa1`|&$MO68NC@F6VNj7GMW|FBWP%{_R81-~ zj+S7Oi3pT|@L)oC7*ZfWcqmsowiN9MwYHFn8lc)%7>GbYQ;{P<6io5T0LmfB1qjAG zn2RJBB)NRty~`yza>-=o_B1-*_dh!~do#DM`Tb`1c4vPk{(nd^2}B?Q5r{y3Fc8~K zx~eMp0Yh?DJu^`4)ewJp_K}cBIwa)ayakJvEMAzHNWBg7lNT>poIHOUy~~adRq6;? zv*3PU3XQLJ@*VsAgPdMJ5kPp-R_eW;(htC})!Dqg%}O1oR#ga|rt$3uM)*Coeh$1& zPgbC-gDu;m)PG6p-Gdo4zP|&#{f0aZoTDda5Qfr6hm<-+y}<#2>;eY(4S5zQVq@VL z_dP(40%QG#Bm&piSTBq>1ah;g?QXDng}|(i330xcl$7*BUS5*c8+m#Awaxat=Dl0V zX4QAabAf{#TNp7s_OErVLGoq~>-n=M_SDl?b-7kk0)Mpd7O)Y{gMfq{GsbPiT< zb=U{wenit^-aSUbruUBj3#pMQ`y9|#a+53sA?(r30$&!0V4*Ai0O z8T06FDUjU?#1Uq)lFW3UzqaTf9QS7GkgVGw?@kC1ft(vfzk=%_~R_5M(c4ZUf>BRb%LGt5T=gvYN`ayG~yj z4}UoW3~2+x2UX{Cx>}*SUhf!ph_iuwHrCaBI=a~xILxN98tz9pz&)q_tW-K(MXQ>h(YrwQ7f|zD3kJ8`RTAzxyWkr+fT_+fyKe|IbbXju@?fw@1FAEs% zALPYOaMhc7zkn+>pQQ8meW3N=L6XyytEI<-lwOw_lApx|hm0B)^h6;Is3=q=`3I@K zR$5W%C@=n;dZoo>6_pibpF7;+{l9&OFHx_e_)CZOrOI2^^!=qpboE8f?o(AXrUlX* zc|a$VZU-cP`A2UAvbNJzzn{K6}j?H6s-E3_3{zH&MLLNUGjtb%I{-LVD-C^dyi&(y>daI2zxlL29ah5b}5^Fw;SI zHR}QFtYeG*0enck-f9|+?-ix)_7}n@fs^!91@xzXj}8E*s5eN>pz&oG0Z9M~*jP7= zrjHJJAcF$}*{9S{*VP!*yj^+`p$v*Wc>=L z%`~8YnX145h<9#CeprukYR$!-s|lQ!9GYbTA3+hw!amP{;TJ3BZ!K#*5} zy=`1a%78g^w+?uV4SjSYBHTgu;tIVX?H-a7lL7>J6?mx)NTq`3m;VABYl2uzW@<&c zJmy91hScT-2yzUV+6ILE@e{h+2oy9y+ywgo-Q$S(u!Kg}_BzC?E!H6xD}ssDI? z{vzL}rc-MwkV{wdF@nt?t|5uCBxoLU=K1dw5)YiECub)Nq26?0I$a$vC;3m{ztroa zenqVdo;ABS`y;(ML?4nZDYTo+_87Jwa^hu&zmWPtz-vv10)&=PtDqZ@SVi7iJNyfh z>wzuQDhW~psh0-4K&`ciqZh=PC>K&y(2n!WsNt4F(wO1yD5h4 zhh!@?#y?1QN^08LHLI2{r{3meE7R7dt^CanYTZbGW?WQKHnq~1tzNTc^|F5`T};vU zr!KukE!(Qe55;fzn9Z`jm*Z7-728++`pF;o0WrM#+mi=$lQ9Qw90C!DKsrF*0#v+r UAh4YlNdN!<07*qoM6N<$f(CDo?f?J) delta 1861 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