typst_library/introspection/location.rs
1use std::fmt::{self, Debug, Formatter};
2use std::num::NonZeroUsize;
3
4use ecow::EcoString;
5
6use crate::engine::Engine;
7use crate::foundations::{Repr, func, scope, ty};
8use crate::layout::Position;
9use crate::model::Numbering;
10
11/// Identifies an element in the document.
12///
13/// A location uniquely identifies an element in the document and lets you
14/// access its absolute position on the pages. You can retrieve the current
15/// location with the [`here`] function and the location of a queried or shown
16/// element with the [`location()`]($content.location) method on content.
17///
18/// # Locatable elements { #locatable }
19/// Currently, only a subset of element functions is locatable. Aside from
20/// headings and figures, this includes equations, references, quotes and all
21/// elements with an explicit label. As a result, you _can_ query for e.g.
22/// [`strong`] elements, but you will find only those that have an explicit
23/// label attached to them. This limitation will be resolved in the future.
24#[ty(scope)]
25#[derive(Copy, Clone, Eq, PartialEq, Hash)]
26pub struct Location(u128);
27
28impl Location {
29 /// Create a new location from a unique hash.
30 pub fn new(hash: u128) -> Self {
31 Self(hash)
32 }
33
34 /// Extract the raw hash.
35 pub fn hash(self) -> u128 {
36 self.0
37 }
38
39 /// Produces a well-known variant of this location.
40 ///
41 /// This is a synthetic location created from another one and is used, for
42 /// example, in bibliography management to create individual linkable
43 /// locations for reference entries from the bibliography's location.
44 pub fn variant(self, n: usize) -> Self {
45 Self(typst_utils::hash128(&(self.0, n)))
46 }
47}
48
49#[scope]
50impl Location {
51 /// Returns the page number for this location.
52 ///
53 /// Note that this does not return the value of the [page counter]($counter)
54 /// at this location, but the true page number (starting from one).
55 ///
56 /// If you want to know the value of the page counter, use
57 /// `{counter(page).at(loc)}` instead.
58 ///
59 /// Can be used with [`here`] to retrieve the physical page position
60 /// of the current context:
61 /// ```example
62 /// #context [
63 /// I am located on
64 /// page #here().page()
65 /// ]
66 /// ```
67 #[func]
68 pub fn page(self, engine: &mut Engine) -> NonZeroUsize {
69 engine.introspector.page(self)
70 }
71
72 /// Returns a dictionary with the page number and the x, y position for this
73 /// location. The page number starts at one and the coordinates are measured
74 /// from the top-left of the page.
75 ///
76 /// If you only need the page number, use `page()` instead as it allows
77 /// Typst to skip unnecessary work.
78 #[func]
79 pub fn position(self, engine: &mut Engine) -> Position {
80 engine.introspector.position(self)
81 }
82
83 /// Returns the page numbering pattern of the page at this location. This
84 /// can be used when displaying the page counter in order to obtain the
85 /// local numbering. This is useful if you are building custom indices or
86 /// outlines.
87 ///
88 /// If the page numbering is set to `{none}` at that location, this function
89 /// returns `{none}`.
90 #[func]
91 pub fn page_numbering(self, engine: &mut Engine) -> Option<Numbering> {
92 engine.introspector.page_numbering(self).cloned()
93 }
94}
95
96impl Debug for Location {
97 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
98 if f.alternate() {
99 write!(f, "Location({})", self.0)
100 } else {
101 // Print a shorter version by default to make it more readable.
102 let truncated = self.0 as u16;
103 write!(f, "Location({truncated})")
104 }
105 }
106}
107
108impl Repr for Location {
109 fn repr(&self) -> EcoString {
110 "..".into()
111 }
112}
113
114/// Can be used to have a location as a key in an ordered set or map.
115///
116/// [`Location`] itself does not implement [`Ord`] because comparing hashes like
117/// this has no semantic meaning. The potential for misuse (e.g. checking
118/// whether locations have a particular relative ordering) is relatively high.
119///
120/// Still, it can be useful to have orderable locations for things like sets.
121/// That's where this type comes in.
122#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
123pub struct LocationKey(u128);
124
125impl LocationKey {
126 /// Create a location key from a location.
127 pub fn new(location: Location) -> Self {
128 Self(location.0)
129 }
130}
131
132impl From<Location> for LocationKey {
133 fn from(location: Location) -> Self {
134 Self::new(location)
135 }
136}
137
138/// Make this element available in the introspector.
139pub trait Locatable {}
140
141/// Make this element not queriable for the user.
142pub trait Unqueriable: Locatable {}
143
144/// Marks this element as tagged in PDF files.
145pub trait Tagged {}