typst_library/
lib.rs

1//! Typst's standard library.
2//!
3//! This crate also contains all of the compiler's central type definitions as
4//! these are interwoven with the standard library types.
5//!
6//! In contrast to the _types,_ most of the compilation _behaviour_ is split out
7//! into separate crates (`typst-eval`, `typst-realize`, `typst-layout`, etc.)
8//!
9//! Note that, unless you are working on the compiler itself, you will rarely
10//! need to interact with this crate, as it is fully reexported by the `typst`
11//! crate.
12
13extern crate self as typst_library;
14
15pub mod diag;
16pub mod engine;
17pub mod foundations;
18pub mod introspection;
19pub mod layout;
20pub mod loading;
21pub mod math;
22pub mod model;
23pub mod pdf;
24pub mod routines;
25pub mod symbols;
26pub mod text;
27pub mod visualize;
28
29use std::ops::{Deref, Range};
30
31use serde::{Deserialize, Serialize};
32use typst_syntax::{FileId, Source, Span};
33use typst_utils::{LazyHash, SmallBitSet};
34
35use crate::diag::FileResult;
36use crate::foundations::{Array, Binding, Bytes, Datetime, Dict, Module, Scope, Styles};
37use crate::layout::{Alignment, Dir};
38use crate::routines::Routines;
39use crate::text::{Font, FontBook};
40use crate::visualize::Color;
41
42/// The environment in which typesetting occurs.
43///
44/// All loading functions (`main`, `source`, `file`, `font`) should perform
45/// internal caching so that they are relatively cheap on repeated invocations
46/// with the same argument. [`Source`], [`Bytes`], and [`Font`] are
47/// all reference-counted and thus cheap to clone.
48///
49/// The compiler doesn't do the caching itself because the world has much more
50/// information on when something can change. For example, fonts typically don't
51/// change and can thus even be cached across multiple compilations (for
52/// long-running applications like `typst watch`). Source files on the other
53/// hand can change and should thus be cleared after each compilation. Advanced
54/// clients like language servers can also retain the source files and
55/// [edit](Source::edit) them in-place to benefit from better incremental
56/// performance.
57#[comemo::track]
58pub trait World: Send + Sync {
59    /// The standard library.
60    ///
61    /// Can be created through `Library::build()`.
62    fn library(&self) -> &LazyHash<Library>;
63
64    /// Metadata about all known fonts.
65    fn book(&self) -> &LazyHash<FontBook>;
66
67    /// Get the file id of the main source file.
68    fn main(&self) -> FileId;
69
70    /// Try to access the specified source file.
71    fn source(&self, id: FileId) -> FileResult<Source>;
72
73    /// Try to access the specified file.
74    fn file(&self, id: FileId) -> FileResult<Bytes>;
75
76    /// Try to access the font with the given index in the font book.
77    fn font(&self, index: usize) -> Option<Font>;
78
79    /// Get the current date.
80    ///
81    /// If no offset is specified, the local date should be chosen. Otherwise,
82    /// the UTC date should be chosen with the corresponding offset in hours.
83    ///
84    /// If this function returns `None`, Typst's `datetime` function will
85    /// return an error.
86    fn today(&self, offset: Option<i64>) -> Option<Datetime>;
87}
88
89macro_rules! world_impl {
90    ($W:ident for $ptr:ty) => {
91        impl<$W: World> World for $ptr {
92            fn library(&self) -> &LazyHash<Library> {
93                self.deref().library()
94            }
95
96            fn book(&self) -> &LazyHash<FontBook> {
97                self.deref().book()
98            }
99
100            fn main(&self) -> FileId {
101                self.deref().main()
102            }
103
104            fn source(&self, id: FileId) -> FileResult<Source> {
105                self.deref().source(id)
106            }
107
108            fn file(&self, id: FileId) -> FileResult<Bytes> {
109                self.deref().file(id)
110            }
111
112            fn font(&self, index: usize) -> Option<Font> {
113                self.deref().font(index)
114            }
115
116            fn today(&self, offset: Option<i64>) -> Option<Datetime> {
117                self.deref().today(offset)
118            }
119        }
120    };
121}
122
123world_impl!(W for std::boxed::Box<W>);
124world_impl!(W for std::sync::Arc<W>);
125world_impl!(W for &W);
126
127/// Helper methods on [`World`] implementations.
128pub trait WorldExt {
129    /// Get the byte range for a span.
130    ///
131    /// Returns `None` if the `Span` does not point into any file.
132    fn range(&self, span: Span) -> Option<Range<usize>>;
133}
134
135impl<T: World + ?Sized> WorldExt for T {
136    fn range(&self, span: Span) -> Option<Range<usize>> {
137        span.range().or_else(|| self.source(span.id()?).ok()?.range(span))
138    }
139}
140
141/// Definition of Typst's standard library.
142///
143/// To create and configure the standard library, use the `LibraryExt` trait
144/// and call
145/// - `Library::default()` for a standard configuration
146/// - `Library::builder().build()` if you want to customize the library
147#[derive(Debug, Clone, Hash)]
148pub struct Library {
149    /// The module that contains the definitions that are available everywhere.
150    pub global: Module,
151    /// The module that contains the definitions available in math mode.
152    pub math: Module,
153    /// The default style properties (for page size, font selection, and
154    /// everything else configurable via set and show rules).
155    pub styles: Styles,
156    /// The standard library as a value. Used to provide the `std` module.
157    pub std: Binding,
158    /// In-development features that were enabled.
159    pub features: Features,
160}
161
162/// Configurable builder for the standard library.
163///
164/// Constructed via the `LibraryExt` trait.
165#[derive(Debug, Clone)]
166pub struct LibraryBuilder {
167    routines: &'static Routines,
168    inputs: Option<Dict>,
169    features: Features,
170}
171
172impl LibraryBuilder {
173    /// Creates a new builder.
174    #[doc(hidden)]
175    pub fn from_routines(routines: &'static Routines) -> Self {
176        Self {
177            routines,
178            inputs: None,
179            features: Features::default(),
180        }
181    }
182
183    /// Configure the inputs visible through `sys.inputs`.
184    pub fn with_inputs(mut self, inputs: Dict) -> Self {
185        self.inputs = Some(inputs);
186        self
187    }
188
189    /// Configure in-development features that should be enabled.
190    ///
191    /// No guarantees whatsover!
192    pub fn with_features(mut self, features: Features) -> Self {
193        self.features = features;
194        self
195    }
196
197    /// Consumes the builder and returns a `Library`.
198    pub fn build(self) -> Library {
199        let math = math::module();
200        let inputs = self.inputs.unwrap_or_default();
201        let global = global(self.routines, math.clone(), inputs, &self.features);
202        Library {
203            global: global.clone(),
204            math,
205            styles: Styles::new(),
206            std: Binding::detached(global),
207            features: self.features,
208        }
209    }
210}
211
212/// A selection of in-development features that should be enabled.
213///
214/// Can be collected from an iterator of [`Feature`]s.
215#[derive(Debug, Default, Clone, Hash)]
216pub struct Features(SmallBitSet);
217
218impl Features {
219    /// Check whether the given feature is enabled.
220    pub fn is_enabled(&self, feature: Feature) -> bool {
221        self.0.contains(feature as usize)
222    }
223}
224
225impl FromIterator<Feature> for Features {
226    fn from_iter<T: IntoIterator<Item = Feature>>(iter: T) -> Self {
227        let mut set = SmallBitSet::default();
228        for feature in iter {
229            set.insert(feature as usize);
230        }
231        Self(set)
232    }
233}
234
235/// An in-development feature that should be enabled.
236#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
237#[non_exhaustive]
238pub enum Feature {
239    Html,
240    A11yExtras,
241}
242
243/// A group of related standard library definitions.
244#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
245#[serde(rename_all = "kebab-case")]
246pub enum Category {
247    Foundations,
248    Introspection,
249    Layout,
250    DataLoading,
251    Math,
252    Model,
253    Symbols,
254    Text,
255    Visualize,
256    Pdf,
257    Html,
258    Svg,
259    Png,
260}
261
262impl Category {
263    /// The kebab-case name of the category.
264    pub fn name(&self) -> &'static str {
265        match self {
266            Self::Foundations => "foundations",
267            Self::Introspection => "introspection",
268            Self::Layout => "layout",
269            Self::DataLoading => "data-loading",
270            Self::Math => "math",
271            Self::Model => "model",
272            Self::Symbols => "symbols",
273            Self::Text => "text",
274            Self::Visualize => "visualize",
275            Self::Pdf => "pdf",
276            Self::Html => "html",
277            Self::Svg => "svg",
278            Self::Png => "png",
279        }
280    }
281}
282
283/// Construct the module with global definitions.
284fn global(
285    routines: &Routines,
286    math: Module,
287    inputs: Dict,
288    features: &Features,
289) -> Module {
290    let mut global = Scope::deduplicating();
291
292    self::foundations::define(&mut global, inputs, features);
293    self::model::define(&mut global);
294    self::text::define(&mut global);
295    self::layout::define(&mut global);
296    self::visualize::define(&mut global);
297    self::introspection::define(&mut global);
298    self::loading::define(&mut global);
299    self::symbols::define(&mut global);
300
301    global.define("math", math);
302    global.define("pdf", self::pdf::module(features));
303    if features.is_enabled(Feature::Html) {
304        global.define("html", (routines.html_module)());
305    }
306
307    prelude(&mut global);
308
309    Module::new("global", global)
310}
311
312/// Defines scoped values that are globally available, too.
313fn prelude(global: &mut Scope) {
314    global.define("black", Color::BLACK);
315    global.define("gray", Color::GRAY);
316    global.define("silver", Color::SILVER);
317    global.define("white", Color::WHITE);
318    global.define("navy", Color::NAVY);
319    global.define("blue", Color::BLUE);
320    global.define("aqua", Color::AQUA);
321    global.define("teal", Color::TEAL);
322    global.define("eastern", Color::EASTERN);
323    global.define("purple", Color::PURPLE);
324    global.define("fuchsia", Color::FUCHSIA);
325    global.define("maroon", Color::MAROON);
326    global.define("red", Color::RED);
327    global.define("orange", Color::ORANGE);
328    global.define("yellow", Color::YELLOW);
329    global.define("olive", Color::OLIVE);
330    global.define("green", Color::GREEN);
331    global.define("lime", Color::LIME);
332    global.define("luma", Color::luma_data());
333    global.define("oklab", Color::oklab_data());
334    global.define("oklch", Color::oklch_data());
335    global.define("rgb", Color::rgb_data());
336    global.define("cmyk", Color::cmyk_data());
337    global.define("range", Array::range_data());
338    global.define("ltr", Dir::LTR);
339    global.define("rtl", Dir::RTL);
340    global.define("ttb", Dir::TTB);
341    global.define("btt", Dir::BTT);
342    global.define("start", Alignment::START);
343    global.define("left", Alignment::LEFT);
344    global.define("center", Alignment::CENTER);
345    global.define("right", Alignment::RIGHT);
346    global.define("end", Alignment::END);
347    global.define("top", Alignment::TOP);
348    global.define("horizon", Alignment::HORIZON);
349    global.define("bottom", Alignment::BOTTOM);
350}