diff options
| author | +merlan #flirora <uruwi@protonmail.com> | 2024-07-17 04:27:46 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-07-17 08:27:46 +0000 |
| commit | df56a2d20ddae5f4ea9aa39a4a1a8b7b11cf7c85 (patch) | |
| tree | 89b351fe8bb84e1477968397f8aed1ec8eca6061 /crates | |
| parent | 993e7a45a9f5e2a18f12aed115d8d9f07557f524 (diff) | |
Allow absolute lengths in `scale` (#4271)
Co-authored-by: Laurenz <laurmaedje@gmail.com>
Diffstat (limited to 'crates')
| -rw-r--r-- | crates/typst/src/layout/ratio.rs | 2 | ||||
| -rw-r--r-- | crates/typst/src/layout/transform.rs | 99 |
2 files changed, 87 insertions, 14 deletions
diff --git a/crates/typst/src/layout/ratio.rs b/crates/typst/src/layout/ratio.rs index 2d791f2d..020d689a 100644 --- a/crates/typst/src/layout/ratio.rs +++ b/crates/typst/src/layout/ratio.rs @@ -70,7 +70,7 @@ impl Ratio { impl Debug for Ratio { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{:?}%", self.get()) + write!(f, "{:?}%", self.get() * 100.0) } } diff --git a/crates/typst/src/layout/transform.rs b/crates/typst/src/layout/transform.rs index 7172466f..ad3668d1 100644 --- a/crates/typst/src/layout/transform.rs +++ b/crates/typst/src/layout/transform.rs @@ -1,7 +1,11 @@ -use crate::diag::SourceResult; +use std::ops::Div; + +use once_cell::unsync::Lazy; + +use crate::diag::{bail, SourceResult}; use crate::engine::Engine; use crate::foundations::{ - elem, Content, NativeElement, Packed, Resolve, Show, StyleChain, + cast, elem, Content, NativeElement, Packed, Resolve, Show, Smart, StyleChain, }; use crate::introspection::Locator; use crate::layout::{ @@ -188,15 +192,15 @@ pub struct ScaleElem { let all = args.find()?; args.named("x")?.or(all) )] - #[default(Ratio::one())] - pub x: Ratio, + #[default(Smart::Custom(ScaleAmount::Ratio(Ratio::one())))] + pub x: Smart<ScaleAmount>, /// The vertical scaling factor. /// /// The body will be mirrored vertically if the parameter is negative. #[parse(args.named("y")?.or(all))] - #[default(Ratio::one())] - pub y: Ratio, + #[default(Smart::Custom(ScaleAmount::Ratio(Ratio::one())))] + pub y: Smart<ScaleAmount>, /// The origin of the transformation. /// @@ -242,12 +246,9 @@ fn layout_scale( styles: StyleChain, region: Region, ) -> SourceResult<Frame> { - let sx = elem.x(styles); - let sy = elem.y(styles); - let align = elem.origin(styles).resolve(styles); - // Compute the new region's approximate size. - let size = region.size.zip_map(Axes::new(sx, sy), |r, s| s.of(r)).map(Abs::abs); + 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); measure_and_layout( engine, @@ -256,12 +257,84 @@ fn layout_scale( size, styles, elem.body(), - Transform::scale(sx, sy), - align, + Transform::scale(scale.x, scale.y), + elem.origin(styles).resolve(styles), elem.reflow(styles), ) } +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +enum ScaleAmount { + Ratio(Ratio), + Length(Length), +} + +impl Packed<ScaleElem> { + /// Resolves scale parameters, preserving aspect ratio if one of the scales is set to `auto`. + fn resolve_scale( + &self, + engine: &mut Engine, + locator: Locator, + container: Size, + styles: StyleChain, + ) -> SourceResult<Axes<Ratio>> { + fn resolve_axis( + axis: Smart<ScaleAmount>, + body: impl Fn() -> SourceResult<Abs>, + styles: StyleChain, + ) -> SourceResult<Smart<Ratio>> { + Ok(match axis { + Smart::Auto => Smart::Auto, + Smart::Custom(amt) => Smart::Custom(match amt { + ScaleAmount::Ratio(ratio) => ratio, + ScaleAmount::Length(length) => { + let length = length.resolve(styles); + Ratio::new(length.div(body()?)) + } + }), + }) + } + + let size = Lazy::new(|| { + let pod = Regions::one(container, Axes::splat(false)); + let frame = self.body().layout(engine, locator, styles, pod)?.into_frame(); + SourceResult::Ok(frame.size()) + }); + + let x = resolve_axis( + self.x(styles), + || size.as_ref().map(|size| size.x).map_err(Clone::clone), + styles, + )?; + + let y = resolve_axis( + self.y(styles), + || size.as_ref().map(|size| size.y).map_err(Clone::clone), + styles, + )?; + + match (x, y) { + (Smart::Auto, Smart::Auto) => { + bail!(self.span(), "x and y cannot both be auto") + } + (Smart::Custom(x), Smart::Custom(y)) => Ok(Axes::new(x, y)), + (Smart::Auto, Smart::Custom(v)) | (Smart::Custom(v), Smart::Auto) => { + Ok(Axes::splat(v)) + } + } + } +} + +cast! { + ScaleAmount, + self => match self { + ScaleAmount::Ratio(ratio) => ratio.into_value(), + ScaleAmount::Length(length) => length.into_value(), + }, + ratio: Ratio => ScaleAmount::Ratio(ratio), + length: Length => ScaleAmount::Length(length), +} + /// A scale-skew-translate transformation. #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub struct Transform { |
