diff --git a/crates/typst/src/layout/measure.rs b/crates/typst/src/layout/measure.rs index 89054925c..7e8ebf584 100644 --- a/crates/typst/src/layout/measure.rs +++ b/crates/typst/src/layout/measure.rs @@ -2,17 +2,19 @@ use comemo::Tracked; use crate::diag::{At, SourceResult}; use crate::engine::Engine; -use crate::foundations::{dict, func, Content, Context, Dict, StyleChain, Styles}; -use crate::layout::{Abs, Axes, LayoutMultiple, Regions, Size}; +use crate::foundations::{ + dict, func, Content, Context, Dict, Resolve, Smart, StyleChain, Styles, +}; +use crate::layout::{Abs, Axes, LayoutMultiple, Length, Regions, Size}; use crate::syntax::Span; /// Measures the layouted size of content. /// -/// The `measure` function lets you determine the layouted size of content. Note -/// that an infinite space is assumed, therefore the measured height/width may -/// not necessarily match the final height/width of the measured content. If you -/// want to measure in the current layout dimensions, you can combine `measure` -/// and [`layout`]. +/// The `measure` function lets you determine the layouted size of content. +/// By default an infinite space is assumed, so the measured dimensions may +/// not necessarily match the final dimensions of the content. +/// If you want to measure in the current layout dimensions, you can combine +/// `measure` and [`layout`]. /// /// # Example /// The same content can have a different size depending on the [context] that @@ -48,6 +50,29 @@ pub fn measure( context: Tracked, /// The callsite span. span: Span, + /// The width available to layout the content. + /// + /// Defaults to `{auto}`, which denotes an infinite width. + /// + /// Using the `width` and `height` parameters of this function is + /// different from measuring a [`box`] containing the content; + /// the former will get the dimensions of the inner content + /// instead of the dimensions of the box: + /// + /// ```example + /// #context measure(lorem(100), width: 400pt) + /// + /// #context measure(block(lorem(100), width: 400pt)) + /// ``` + #[named] + #[default(Smart::Auto)] + width: Smart, + /// The height available to layout the content. + /// + /// Defaults to `{auto}`, which denotes an infinite height. + #[named] + #[default(Smart::Auto)] + height: Smart, /// The content whose size to measure. content: Content, /// _Compatibility:_ This argument only exists for compatibility with @@ -60,7 +85,12 @@ pub fn measure( None => context.styles().at(span)?, }; - let pod = Regions::one(Axes::splat(Abs::inf()), Axes::splat(false)); + let available = Axes::new( + width.resolve(styles).unwrap_or(Abs::inf()), + height.resolve(styles).unwrap_or(Abs::inf()), + ); + + let pod = Regions::one(available, Axes::splat(false)); let frame = content.measure(engine, styles, pod)?.into_frame(); let Size { x, y } = frame.size(); Ok(dict! { "width" => x, "height" => y }) diff --git a/tests/suite/layout/measure.typ b/tests/suite/layout/measure.typ index 5f82e9150..10c3c8185 100644 --- a/tests/suite/layout/measure.typ +++ b/tests/suite/layout/measure.typ @@ -7,3 +7,16 @@ } #text(10pt, f(6pt, 8pt)) #text(20pt, f(13pt, 14pt)) + +--- measure-given-area --- +// Test `measure` given an area. +#let text = lorem(100) + +#context { + let d1 = measure(text) + assert(d1.width > 2000pt) + assert(d1.height < 10pt) + let d2 = measure(width: 400pt, height: auto, text) + assert(d2.width < 400pt) + assert(d2.height > 50pt) +}