typst_library/layout/layout.rs
1use typst_syntax::Span;
2
3use crate::foundations::{Content, Func, NativeElement, elem, func};
4use crate::introspection::Locatable;
5
6/// Provides access to the current outer container's (or page's, if none)
7/// dimensions (width and height).
8///
9/// Accepts a function that receives a single parameter, which is a dictionary
10/// with keys `width` and `height`, both of type [`length`]. The function is
11/// provided [context], meaning you don't need to use it in combination with the
12/// `context` keyword. This is why [`measure`] can be called in the example
13/// below.
14///
15/// ```example
16/// #let text = lorem(30)
17/// #layout(size => [
18/// #let (height,) = measure(
19/// width: size.width,
20/// text,
21/// )
22/// This text is #height high with
23/// the current page width: \
24/// #text
25/// ])
26/// ```
27///
28/// Note that the `layout` function forces its contents into a [block]-level
29/// container, so placement relative to the page or pagebreaks are not possible
30/// within it.
31///
32/// If the `layout` call is placed inside a box with a width of `{800pt}` and a
33/// height of `{400pt}`, then the specified function will be given the argument
34/// `{(width: 800pt, height: 400pt)}`. If it is placed directly into the page, it
35/// receives the page's dimensions minus its margins. This is mostly useful in
36/// combination with [measurement]($measure).
37///
38/// To retrieve the _remaining_ height of the page rather than its full size,
39/// you can wrap your `layout` call in a `{block(height: 1fr)}`. This works
40/// because the block automatically grows to fill the remaining space (see the
41/// [fraction] documentation for more details).
42///
43/// ```example
44/// #set page(height: 150pt)
45///
46/// #lorem(20)
47///
48/// #block(height: 1fr, layout(size => [
49/// Remaining height: #size.height
50/// ]))
51/// ```
52///
53/// You can also use this function to resolve a [`ratio`] to a fixed length.
54/// This might come in handy if you're building your own layout abstractions.
55///
56/// ```example
57/// #layout(size => {
58/// let half = 50% * size.width
59/// [Half a page is #half wide.]
60/// })
61/// ```
62///
63/// Note that the width or height provided by `layout` will be infinite if the
64/// corresponding page dimension is set to `{auto}`.
65#[func]
66pub fn layout(
67 span: Span,
68 /// A function to call with the outer container's size. Its return value is
69 /// displayed in the document.
70 ///
71 /// The container's size is given as a [dictionary] with the keys `width`
72 /// and `height`.
73 ///
74 /// This function is called once for each time the content returned by
75 /// `layout` appears in the document. This makes it possible to generate
76 /// content that depends on the dimensions of its container.
77 func: Func,
78) -> Content {
79 LayoutElem::new(func).pack().spanned(span)
80}
81
82/// Executes a `layout` call.
83#[elem(Locatable)]
84pub struct LayoutElem {
85 /// The function to call with the outer container's (or page's) size.
86 #[required]
87 pub func: Func,
88}