summaryrefslogtreecommitdiff
path: root/crates/typst-library/src/layout/measure.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/typst-library/src/layout/measure.rs')
-rw-r--r--crates/typst-library/src/layout/measure.rs115
1 files changed, 115 insertions, 0 deletions
diff --git a/crates/typst-library/src/layout/measure.rs b/crates/typst-library/src/layout/measure.rs
new file mode 100644
index 00000000..2fa51b2d
--- /dev/null
+++ b/crates/typst-library/src/layout/measure.rs
@@ -0,0 +1,115 @@
+use comemo::Tracked;
+use typst_syntax::Span;
+
+use crate::diag::{warning, At, SourceResult};
+use crate::engine::Engine;
+use crate::foundations::{
+ dict, func, Content, Context, Dict, Resolve, Smart, StyleChain, Styles,
+};
+use crate::introspection::{Locator, LocatorLink};
+use crate::layout::{Abs, Axes, Length, Region, Size};
+
+/// Measures the layouted size of content.
+///
+/// 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
+/// it is placed into. In the example below, the `[#content]` is of course
+/// bigger when we increase the font size.
+///
+/// ```example
+/// #let content = [Hello!]
+/// #content
+/// #set text(14pt)
+/// #content
+/// ```
+///
+/// For this reason, you can only measure when context is available.
+///
+/// ```example
+/// #let thing(body) = context {
+/// let size = measure(body)
+/// [Width of "#body" is #size.width]
+/// }
+///
+/// #thing[Hey] \
+/// #thing[Welcome]
+/// ```
+///
+/// The measure function returns a dictionary with the entries `width` and
+/// `height`, both of type [`length`].
+#[func(contextual)]
+pub fn measure(
+ /// The engine.
+ engine: &mut Engine,
+ /// The callsite context.
+ context: Tracked<Context>,
+ /// The callsite span.
+ span: Span,
+ /// The width available to layout the content.
+ ///
+ /// Setting this to `{auto}` indicates infinite available width.
+ ///
+ /// Note that using the `width` and `height` parameters of this function is
+ /// different from measuring a sized [`block`] containing the content. In
+ /// the following example, the former will get the dimensions of the inner
+ /// content instead of the dimensions of the block.
+ ///
+ /// ```example
+ /// #context measure(lorem(100), width: 400pt)
+ ///
+ /// #context measure(block(lorem(100), width: 400pt))
+ /// ```
+ #[named]
+ #[default(Smart::Auto)]
+ width: Smart<Length>,
+ /// The height available to layout the content.
+ ///
+ /// Setting this to `{auto}` indicates infinite available height.
+ #[named]
+ #[default(Smart::Auto)]
+ height: Smart<Length>,
+ /// The content whose size to measure.
+ content: Content,
+ /// _Compatibility:_ This argument is deprecated. It only exists for
+ /// compatibility with Typst 0.10 and lower and shouldn't be used anymore.
+ #[default]
+ styles: Option<Styles>,
+) -> SourceResult<Dict> {
+ let styles = match &styles {
+ Some(styles) => {
+ engine.sink.warn(warning!(
+ span, "calling `measure` with a styles argument is deprecated";
+ hint: "try removing the styles argument"
+ ));
+ StyleChain::new(styles)
+ }
+ None => context.styles().at(span)?,
+ };
+
+ // Create a pod region with the available space.
+ let pod = Region::new(
+ Axes::new(
+ width.resolve(styles).unwrap_or(Abs::inf()),
+ height.resolve(styles).unwrap_or(Abs::inf()),
+ ),
+ Axes::splat(false),
+ );
+
+ // We put the locator into a special "measurement mode" to ensure that
+ // introspection-driven features within the content continue to work. Read
+ // the "Dealing with measurement" section of the [`Locator`] docs for more
+ // details.
+ let here = context.location().at(span)?;
+ let link = LocatorLink::measure(here);
+ let locator = Locator::link(&link);
+
+ let frame = (engine.routines.layout_frame)(engine, &content, locator, styles, pod)?;
+ let Size { x, y } = frame.size();
+ Ok(dict! { "width" => x, "height" => y })
+}