mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +08:00
Allow absolute lengths in scale
(#4271)
Co-authored-by: Laurenz <laurmaedje@gmail.com>
This commit is contained in:
parent
993e7a45a9
commit
df56a2d20d
@ -70,7 +70,7 @@ impl Ratio {
|
|||||||
|
|
||||||
impl Debug for Ratio {
|
impl Debug for Ratio {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
write!(f, "{:?}%", self.get())
|
write!(f, "{:?}%", self.get() * 100.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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::engine::Engine;
|
||||||
use crate::foundations::{
|
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::introspection::Locator;
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
@ -188,15 +192,15 @@ pub struct ScaleElem {
|
|||||||
let all = args.find()?;
|
let all = args.find()?;
|
||||||
args.named("x")?.or(all)
|
args.named("x")?.or(all)
|
||||||
)]
|
)]
|
||||||
#[default(Ratio::one())]
|
#[default(Smart::Custom(ScaleAmount::Ratio(Ratio::one())))]
|
||||||
pub x: Ratio,
|
pub x: Smart<ScaleAmount>,
|
||||||
|
|
||||||
/// The vertical scaling factor.
|
/// The vertical scaling factor.
|
||||||
///
|
///
|
||||||
/// The body will be mirrored vertically if the parameter is negative.
|
/// The body will be mirrored vertically if the parameter is negative.
|
||||||
#[parse(args.named("y")?.or(all))]
|
#[parse(args.named("y")?.or(all))]
|
||||||
#[default(Ratio::one())]
|
#[default(Smart::Custom(ScaleAmount::Ratio(Ratio::one())))]
|
||||||
pub y: Ratio,
|
pub y: Smart<ScaleAmount>,
|
||||||
|
|
||||||
/// The origin of the transformation.
|
/// The origin of the transformation.
|
||||||
///
|
///
|
||||||
@ -242,12 +246,9 @@ fn layout_scale(
|
|||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
region: Region,
|
region: Region,
|
||||||
) -> SourceResult<Frame> {
|
) -> 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.
|
// 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(
|
measure_and_layout(
|
||||||
engine,
|
engine,
|
||||||
@ -256,12 +257,84 @@ fn layout_scale(
|
|||||||
size,
|
size,
|
||||||
styles,
|
styles,
|
||||||
elem.body(),
|
elem.body(),
|
||||||
Transform::scale(sx, sy),
|
Transform::scale(scale.x, scale.y),
|
||||||
align,
|
elem.origin(styles).resolve(styles),
|
||||||
elem.reflow(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.
|
/// A scale-skew-translate transformation.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub struct Transform {
|
pub struct Transform {
|
||||||
|
BIN
tests/ref/transform-scale-abs-and-auto.png
Normal file
BIN
tests/ref/transform-scale-abs-and-auto.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.6 KiB |
@ -74,7 +74,7 @@ Hello #rotated[World]!\
|
|||||||
Hello #rotated[World]!
|
Hello #rotated[World]!
|
||||||
|
|
||||||
--- transform-scale ---
|
--- transform-scale ---
|
||||||
// Test that scaling impact layout.
|
// Test that scaling impacts layout.
|
||||||
#set page(width: 200pt)
|
#set page(width: 200pt)
|
||||||
#set text(size: 32pt)
|
#set text(size: 32pt)
|
||||||
#let scaled(body) = box(scale(
|
#let scaled(body) = box(scale(
|
||||||
@ -104,3 +104,14 @@ Hello #scaled[World]!\
|
|||||||
|
|
||||||
#set scale(reflow: true)
|
#set scale(reflow: true)
|
||||||
Hello #scaled[World]!
|
Hello #scaled[World]!
|
||||||
|
|
||||||
|
--- transform-scale-abs-and-auto ---
|
||||||
|
// Test scaling by absolute lengths and auto.
|
||||||
|
#set page(width: 200pt, height: 200pt)
|
||||||
|
#let cylinder = image("/assets/images/cylinder.svg")
|
||||||
|
|
||||||
|
#cylinder
|
||||||
|
#scale(x: 100pt, y: 50pt, reflow: true, cylinder)
|
||||||
|
#scale(x: auto, y: 50pt, reflow: true, cylinder)
|
||||||
|
#scale(x: 100pt, y: auto, reflow: true, cylinder)
|
||||||
|
#scale(x: 150%, y: auto, reflow: true, cylinder)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user