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/// Elements that are automatically assigned a location are called _locatable._
20/// For efficiency reasons, not all elements are locatable.
21///
22/// - In the [Model category]($category/model), most elements are locatable.
23/// This is because semantic elements like [headings]($heading) and
24/// [figures]($figure) are often used with introspection.
25///
26/// - In the [Text category]($category/text), the [`raw`] element, and the
27/// decoration elements [`underline`], [`overline`], [`strike`], and
28/// [`highlight`] are locatable as these are also quite semantic in nature.
29///
30/// - In the [Introspection category]($category/introspection), the [`metadata`]
31/// element is locatable as being queried for is its primary purpose.
32///
33/// - In the other categories, most elements are not locatable. Exceptions are
34/// [`math.equation`] and [`image`].
35///
36/// To find out whether a specific element is locatable, you can try to
37/// [`query`] for it.
38///
39/// Note that you can still observe elements that are not locatable in queries
40/// through other means, for instance, when they have a label attached to them.
41#[ty(scope)]
42#[derive(Copy, Clone, Eq, PartialEq, Hash)]
43pub struct Location(u128);
44
45impl Location {
46 /// Create a new location from a unique hash.
47 pub fn new(hash: u128) -> Self {
48 Self(hash)
49 }
50
51 /// Extract the raw hash.
52 pub fn hash(self) -> u128 {
53 self.0
54 }
55
56 /// Produces a well-known variant of this location.
57 ///
58 /// This is a synthetic location created from another one and is used, for
59 /// example, in bibliography management to create individual linkable
60 /// locations for reference entries from the bibliography's location.
61 pub fn variant(self, n: usize) -> Self {
62 Self(typst_utils::hash128(&(self.0, n)))
63 }
64}
65
66#[scope]
67impl Location {
68 /// Returns the page number for this location.
69 ///
70 /// Note that this does not return the value of the [page counter]($counter)
71 /// at this location, but the true page number (starting from one).
72 ///
73 /// If you want to know the value of the page counter, use
74 /// `{counter(page).at(loc)}` instead.
75 ///
76 /// Can be used with [`here`] to retrieve the physical page position
77 /// of the current context:
78 /// ```example
79 /// #context [
80 /// I am located on
81 /// page #here().page()
82 /// ]
83 /// ```
84 #[func]
85 pub fn page(self, engine: &mut Engine) -> NonZeroUsize {
86 engine.introspector.page(self)
87 }
88
89 /// Returns a dictionary with the page number and the x, y position for this
90 /// location. The page number starts at one and the coordinates are measured
91 /// from the top-left of the page.
92 ///
93 /// If you only need the page number, use `page()` instead as it allows
94 /// Typst to skip unnecessary work.
95 #[func]
96 pub fn position(self, engine: &mut Engine) -> Position {
97 engine.introspector.position(self)
98 }
99
100 /// Returns the page numbering pattern of the page at this location. This
101 /// can be used when displaying the page counter in order to obtain the
102 /// local numbering. This is useful if you are building custom indices or
103 /// outlines.
104 ///
105 /// If the page numbering is set to `{none}` at that location, this function
106 /// returns `{none}`.
107 #[func]
108 pub fn page_numbering(self, engine: &mut Engine) -> Option<Numbering> {
109 engine.introspector.page_numbering(self).cloned()
110 }
111}
112
113impl Debug for Location {
114 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
115 if f.alternate() {
116 write!(f, "Location({})", self.0)
117 } else {
118 // Print a shorter version by default to make it more readable.
119 let truncated = self.0 as u16;
120 write!(f, "Location({truncated})")
121 }
122 }
123}
124
125impl Repr for Location {
126 fn repr(&self) -> EcoString {
127 "..".into()
128 }
129}
130
131/// Can be used to have a location as a key in an ordered set or map.
132///
133/// [`Location`] itself does not implement [`Ord`] because comparing hashes like
134/// this has no semantic meaning. The potential for misuse (e.g. checking
135/// whether locations have a particular relative ordering) is relatively high.
136///
137/// Still, it can be useful to have orderable locations for things like sets.
138/// That's where this type comes in.
139#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
140pub struct LocationKey(u128);
141
142impl LocationKey {
143 /// Create a location key from a location.
144 pub fn new(location: Location) -> Self {
145 Self(location.0)
146 }
147}
148
149impl From<Location> for LocationKey {
150 fn from(location: Location) -> Self {
151 Self::new(location)
152 }
153}
154
155/// Make this element available in the introspector.
156pub trait Locatable {}
157
158/// Make this element not queriable for the user.
159pub trait Unqueriable: Locatable {}
160
161/// Marks this element as tagged in PDF files.
162pub trait Tagged {}