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 {}