1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
|
use crate::prelude::*;
/// Provides access to the location of content.
///
/// This is useful in combination with [queries]($func/query),
/// [counters]($func/counter), [state]($func/state), and [links]($func/link).
/// See their documentation for more details.
///
/// ```example
/// #locate(loc => [
/// My location: \
/// #loc.position()!
/// ])
/// ```
///
/// ## Methods
/// ### page()
/// Return the page number for this location.
///
/// Note that this does not return the value of the [page counter]($func/counter)
/// at this location, but the true page number (starting from one).
///
/// If you want to know the value of the page counter, use
/// `{counter(page).at(loc)}` instead.
///
/// - returns: integer
///
/// ### position()
/// Return a dictionary with the page number and the x, y position for this
/// location. The page number starts at one and the coordinates are measured
/// from the top-left of the page.
///
/// If you only need the page number, use `page()` instead as it allows Typst
/// to skip unnecessary work.
///
/// - returns: dictionary
///
/// ### page-numbering()
/// Returns the page numbering pattern of the page at this location. This can be
/// used when displaying the page counter in order to obtain the local numbering.
/// This is useful if you are building custom indices or outlines.
///
/// If the page numbering is set to `none` at that location, this function returns `none`.
///
/// - returns: string or function or none
///
/// Display: Locate
/// Category: meta
/// Returns: content
#[func]
pub fn locate(
/// A function that receives a `location`. Its return value is displayed
/// in the document.
///
/// This function is called once for each time the content returned by
/// `locate` appears in the document. That makes it possible to generate
/// content that depends on its own location in the document.
func: Func,
) -> Value {
LocateElem::new(func).pack().into()
}
/// Executes a `locate` call.
///
/// Display: Locate
/// Category: special
#[element(Locatable, Show)]
struct LocateElem {
/// The function to call with the location.
#[required]
func: Func,
}
impl Show for LocateElem {
#[tracing::instrument(name = "LocateElem::show", skip(self, vt))]
fn show(&self, vt: &mut Vt, _: StyleChain) -> SourceResult<Content> {
if !vt.introspector.init() {
return Ok(Content::empty());
}
let location = self.0.location().unwrap();
Ok(self.func().call_vt(vt, [location.into()])?.display())
}
}
/// Provides access to active styles.
///
/// The styles are currently opaque and only useful in combination with the
/// [`measure`]($func/measure) function. See its documentation for more details.
/// In the future, the provided styles might also be directly accessed to look
/// up styles defined by [set rules]($styling/#set-rules).
///
/// ```example
/// #let thing(body) = style(styles => {
/// let size = measure(body, styles)
/// [Width of "#body" is #size.width]
/// })
///
/// #thing[Hey] \
/// #thing[Welcome]
/// ```
///
/// Display: Style
/// Category: meta
/// Returns: content
#[func]
pub fn style(
/// A function to call with the styles. Its return value is displayed
/// in the document.
///
/// This function is called once for each time the content returned by
/// `style` appears in the document. That makes it possible to generate
/// content that depends on the style context it appears in.
func: Func,
) -> Value {
StyleElem::new(func).pack().into()
}
/// Executes a style access.
///
/// Display: Style
/// Category: special
#[element(Show)]
struct StyleElem {
/// The function to call with the styles.
#[required]
func: Func,
}
impl Show for StyleElem {
#[tracing::instrument(name = "StyleElem::show", skip_all)]
fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
Ok(self.func().call_vt(vt, [styles.to_map().into()])?.display())
}
}
/// Provides access to the current outer container's (or page's, if none) size
/// (width and height).
///
/// The given function must accept a single parameter, `size`, which is a
/// dictionary with keys `width` and `height`, both of type
/// [`length`]($type/length).
///
/// ```example
/// #let text = lorem(30)
/// #layout(size => style(styles => [
/// #let (height,) = measure(
/// block(width: size.width, text),
/// styles,
/// )
/// This text is #height high with
/// the current page width: \
/// #text
/// ]))
/// ```
///
/// If the `layout` call is placed inside of a box width a width of `{800pt}`
/// and a height of `{400pt}`, then the specified function will be given the
/// parameter `{(width: 800pt, height: 400pt)}`. If it placed directly into the
/// page it receives the page's dimensions minus its margins. This is mostly
/// useful in combination with [measurement]($func/measure).
///
/// You can also use this function to resolve [`ratio`]($type/ratio) to fixed
/// lengths. This might come in handy if you're building your own layout
/// abstractions.
///
/// ```example
/// #layout(size => {
/// let half = 50% * size.width
/// [Half a page is #half wide.]
/// })
/// ```
///
/// Note that this function will provide an infinite width or height if one of
/// the page width or height is `auto`, respectively.
///
/// Display: Layout
/// Category: meta
/// Returns: content
#[func]
pub fn layout(
/// A function to call with the outer container's size. Its return value is
/// displayed in the document.
///
/// The container's size is given as a [dictionary]($type/dictionary) with
/// the keys `width` and `height`.
///
/// This function is called once for each time the content returned by
/// `layout` appears in the document. That makes it possible to generate
/// content that depends on the size of the container it is inside of.
func: Func,
) -> Value {
LayoutElem::new(func).pack().into()
}
/// Executes a `layout` call.
///
/// Display: Layout
/// Category: special
#[element(Layout)]
struct LayoutElem {
/// The function to call with the outer container's (or page's) size.
#[required]
func: Func,
}
impl Layout for LayoutElem {
#[tracing::instrument(name = "LayoutElem::layout", skip_all)]
fn layout(
&self,
vt: &mut Vt,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
// Gets the current region's base size, which will be the size of the outer container,
// or of the page if there is no such container.
let Size { x, y } = regions.base();
let size_dict = dict! { "width" => x, "height" => y }.into();
let result = self
.func()
.call_vt(vt, [size_dict])? // calls func(size)
.display();
result.layout(vt, styles, regions)
}
}
|