summaryrefslogtreecommitdiff
path: root/src/lib.rs
blob: 572a054178b8736c954ba37b8f78ee50a2548826 (plain) (blame)
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
//! The compiler for the _Typst_ typesetting language.
//!
//! # Steps
//! - **Parsing:** The parsing step first transforms a plain string into an
//!   [iterator of tokens][tokens]. This token stream is [parsed] into a [syntax
//!   tree]. The tree itself is untyped, but the [AST] module provides a typed
//!   layer over it.
//! - **Evaluation:** The next step is to [evaluate] the markup. This produces a
//!   [module], consisting of a scope of values that were exported by the code
//!   and [content], a hierarchical, styled representation of the text,
//!   structure, layouts, etc. of the module. The nodes of the content tree are
//!   well structured and order-independent and thus much better suited for
//!   layouting than the raw markup.
//! - **Layouting:** Next, the content is [layouted] into a portable version of
//!   the typeset document. The output of this is a collection of [`Frame`]s
//!   (one per page), ready for exporting.
//! - **Exporting:** The finished layout can be exported into a supported
//!   format. Currently, the only supported output format is [PDF].
//!
//! [tokens]: parse::Tokens
//! [parsed]: parse::parse
//! [syntax tree]: syntax::SyntaxNode
//! [AST]: syntax::ast
//! [evaluate]: eval::evaluate
//! [module]: eval::Module
//! [content]: model::Content
//! [layouted]: model::layout
//! [PDF]: export::pdf

#![allow(clippy::len_without_is_empty)]
#![allow(clippy::or_fun_call)]
#![allow(clippy::try_err)]

#[macro_use]
pub mod util;
#[macro_use]
pub mod geom;
#[macro_use]
pub mod diag;
#[macro_use]
pub mod eval;
pub mod export;
pub mod font;
pub mod frame;
pub mod image;
pub mod library;
pub mod loading;
pub mod model;
pub mod parse;
pub mod source;
pub mod syntax;

use std::path::PathBuf;
use std::sync::Arc;

use crate::diag::TypResult;
use crate::eval::Scope;
use crate::font::FontStore;
use crate::frame::Frame;
use crate::image::ImageStore;
use crate::loading::Loader;
use crate::model::StyleMap;
use crate::source::{SourceId, SourceStore};

/// Typeset a source file into a collection of layouted frames.
///
/// Returns either a vector of frames representing individual pages or
/// diagnostics in the form of a vector of error message with file and span
/// information.
pub fn typeset(ctx: &mut Context, id: SourceId) -> TypResult<Vec<Frame>> {
    let module = eval::evaluate(ctx, id, vec![])?;
    model::layout(ctx, &module.content)
}

/// The core context which holds the configuration and stores.
pub struct Context {
    /// The loader for fonts and files.
    pub loader: Arc<dyn Loader>,
    /// Stores loaded source files.
    pub sources: SourceStore,
    /// Stores parsed fonts.
    pub fonts: FontStore,
    /// Stores decoded images.
    pub images: ImageStore,
    /// The context's configuration.
    config: Config,
}

impl Context {
    /// Create a new context.
    pub fn new(loader: Arc<dyn Loader>, config: Config) -> Self {
        Self {
            loader: Arc::clone(&loader),
            sources: SourceStore::new(Arc::clone(&loader)),
            fonts: FontStore::new(Arc::clone(&loader)),
            images: ImageStore::new(loader),
            config,
        }
    }
}

/// Compilation configuration.
pub struct Config {
    /// The compilation root.
    pub root: PathBuf,
    /// The standard library scope.
    pub std: Arc<Scope>,
    /// The default styles.
    pub styles: Arc<StyleMap>,
}

impl Config {
    /// Create a new configuration builder.
    pub fn builder() -> ConfigBuilder {
        ConfigBuilder::default()
    }
}

impl Default for Config {
    fn default() -> Self {
        Self::builder().build()
    }
}

/// A builder for a [`Config`].
///
/// This struct is created by [`Config::builder`].
#[derive(Debug, Default, Clone)]
pub struct ConfigBuilder {
    root: PathBuf,
    std: Option<Arc<Scope>>,
    styles: Option<Arc<StyleMap>>,
}

impl ConfigBuilder {
    /// The compilation root, relative to which absolute paths are.
    ///
    /// Default: Empty path.
    pub fn root(&mut self, root: impl Into<PathBuf>) -> &mut Self {
        self.root = root.into();
        self
    }

    /// The scope containing definitions that are available everywhere.
    ///
    /// Default: Typst's standard library.
    pub fn std(&mut self, std: impl Into<Arc<Scope>>) -> &mut Self {
        self.std = Some(std.into());
        self
    }

    /// The default properties for page size, font selection and so on.
    ///
    /// Default: Empty style map.
    pub fn styles(&mut self, styles: impl Into<Arc<StyleMap>>) -> &mut Self {
        self.styles = Some(styles.into());
        self
    }

    /// Finish building the configuration.
    pub fn build(&self) -> Config {
        Config {
            root: self.root.clone(),
            std: self.std.clone().unwrap_or_else(|| Arc::new(library::new())),
            styles: self.styles.clone().unwrap_or_default(),
        }
    }
}