typst 0.13.3-arm64-darwin

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,248 @@
1
+ use chrono::{Datelike, Timelike};
2
+ use codespan_reporting::diagnostic::{Diagnostic, Label};
3
+ use codespan_reporting::term::{self, termcolor};
4
+ use ecow::{eco_format, EcoString};
5
+ use typst::diag::{Severity, SourceDiagnostic, StrResult, Warned};
6
+ use typst::foundations::Datetime;
7
+ use typst::html::HtmlDocument;
8
+ use typst::layout::PagedDocument;
9
+ use typst::syntax::{FileId, Source, Span};
10
+ use typst::{World, WorldExt};
11
+
12
+ use crate::world::SystemWorld;
13
+
14
+ type CodespanResult<T> = Result<T, CodespanError>;
15
+ type CodespanError = codespan_reporting::files::Error;
16
+
17
+ impl SystemWorld {
18
+ pub fn compile(
19
+ &mut self,
20
+ format: Option<&str>,
21
+ ppi: Option<f32>,
22
+ pdf_standards: &[typst_pdf::PdfStandard],
23
+ ) -> StrResult<Vec<Vec<u8>>> {
24
+ // Reset everything and ensure that the main file is present.
25
+ self.reset();
26
+ self.source(self.main()).map_err(|err| err.to_string())?;
27
+
28
+ match format.unwrap_or_else(|| "pdf").to_ascii_lowercase().as_str() {
29
+ "html" => {
30
+ let Warned { output, warnings } = typst::compile::<HtmlDocument>(self);
31
+ match output {
32
+ Ok(document) => {
33
+ Ok(vec![export_html(&document, self)?])
34
+ }
35
+ Err(errors) => Err(format_diagnostics(self, &errors, &warnings).unwrap().into()),
36
+ }
37
+ }
38
+ _ => {
39
+ let Warned { output, warnings } = typst::compile::<PagedDocument>(self);
40
+ match output {
41
+ // Export the PDF / PNG.
42
+ Ok(document) => {
43
+ // Assert format is "pdf" or "png" or "svg"
44
+ match format.unwrap_or("pdf").to_ascii_lowercase().as_str() {
45
+ "pdf" => Ok(vec![export_pdf(
46
+ &document,
47
+ self,
48
+ typst_pdf::PdfStandards::new(pdf_standards)?,
49
+ )?]),
50
+ "png" => Ok(export_image(&document, ImageExportFormat::Png, ppi)?),
51
+ "svg" => Ok(export_image(&document, ImageExportFormat::Svg, ppi)?),
52
+ fmt => Err(eco_format!("unknown format: {fmt}")),
53
+ }
54
+ }
55
+ Err(errors) => Err(format_diagnostics(self, &errors, &warnings).unwrap().into()),
56
+ }
57
+ }
58
+ }
59
+ }
60
+ }
61
+
62
+ /// Export to a PDF.
63
+ #[inline]
64
+ fn export_html(
65
+ document: &HtmlDocument,
66
+ world: &SystemWorld,
67
+ ) -> StrResult<Vec<u8>> {
68
+ let html = typst_html::html(document)
69
+ .map_err(|e| match format_diagnostics(world, &e, &[]) {
70
+ Ok(e) => EcoString::from(e),
71
+ Err(err) => eco_format!("failed to print diagnostics ({err})"),
72
+ })?;
73
+ let buffer = html.as_bytes().to_vec();
74
+ Ok(buffer)
75
+ }
76
+
77
+ /// Export to a PDF.
78
+ #[inline]
79
+ fn export_pdf(
80
+ document: &PagedDocument,
81
+ world: &SystemWorld,
82
+ standards: typst_pdf::PdfStandards,
83
+ ) -> StrResult<Vec<u8>> {
84
+ let ident = world.input().to_string_lossy();
85
+ let buffer = typst_pdf::pdf(
86
+ document,
87
+ &typst_pdf::PdfOptions {
88
+ ident: typst::foundations::Smart::Custom(&ident),
89
+ timestamp: now().map(typst_pdf::Timestamp::new_utc),
90
+ standards,
91
+ ..Default::default()
92
+ },
93
+ )
94
+ .map_err(|e| match format_diagnostics(world, &e, &[]) {
95
+ Ok(e) => EcoString::from(e),
96
+ Err(err) => eco_format!("failed to print diagnostics ({err})"),
97
+ })?;
98
+ Ok(buffer)
99
+ }
100
+
101
+ /// Get the current date and time in UTC.
102
+ fn now() -> Option<Datetime> {
103
+ let now = chrono::Local::now().naive_utc();
104
+ Datetime::from_ymd_hms(
105
+ now.year(),
106
+ now.month().try_into().ok()?,
107
+ now.day().try_into().ok()?,
108
+ now.hour().try_into().ok()?,
109
+ now.minute().try_into().ok()?,
110
+ now.second().try_into().ok()?,
111
+ )
112
+ }
113
+
114
+ /// An image format to export in.
115
+ enum ImageExportFormat {
116
+ Png,
117
+ Svg,
118
+ }
119
+
120
+ /// Export the frames to PNGs or SVGs.
121
+ fn export_image(
122
+ document: &PagedDocument,
123
+ fmt: ImageExportFormat,
124
+ ppi: Option<f32>,
125
+ ) -> StrResult<Vec<Vec<u8>>> {
126
+ let mut buffers = Vec::new();
127
+ for page in &document.pages {
128
+ let buffer = match fmt {
129
+ ImageExportFormat::Png => typst_render::render(page, ppi.unwrap_or(144.0) / 72.0)
130
+ .encode_png()
131
+ .map_err(|err| eco_format!("failed to write PNG file ({err})"))?,
132
+ ImageExportFormat::Svg => {
133
+ let svg = typst_svg::svg(page);
134
+ svg.as_bytes().to_vec()
135
+ }
136
+ };
137
+ buffers.push(buffer);
138
+ }
139
+ Ok(buffers)
140
+ }
141
+
142
+ /// Format diagnostic messages.\
143
+ pub fn format_diagnostics(
144
+ world: &SystemWorld,
145
+ errors: &[SourceDiagnostic],
146
+ warnings: &[SourceDiagnostic],
147
+ ) -> Result<String, codespan_reporting::files::Error> {
148
+ let mut w = termcolor::Buffer::no_color();
149
+
150
+ let config = term::Config {
151
+ tab_width: 2,
152
+ ..Default::default()
153
+ };
154
+
155
+ for diagnostic in warnings.iter().chain(errors.iter()) {
156
+ let diag = match diagnostic.severity {
157
+ Severity::Error => Diagnostic::error(),
158
+ Severity::Warning => Diagnostic::warning(),
159
+ }
160
+ .with_message(diagnostic.message.clone())
161
+ .with_notes(
162
+ diagnostic
163
+ .hints
164
+ .iter()
165
+ .map(|e| (eco_format!("hint: {e}")).into())
166
+ .collect(),
167
+ )
168
+ .with_labels(label(world, diagnostic.span).into_iter().collect());
169
+
170
+ term::emit(&mut w, &config, world, &diag)?;
171
+
172
+ // Stacktrace-like helper diagnostics.
173
+ for point in &diagnostic.trace {
174
+ let message = point.v.to_string();
175
+ let help = Diagnostic::help()
176
+ .with_message(message)
177
+ .with_labels(label(world, point.span).into_iter().collect());
178
+
179
+ term::emit(&mut w, &config, world, &help)?;
180
+ }
181
+ }
182
+
183
+ let s = String::from_utf8(w.into_inner()).unwrap();
184
+ Ok(s)
185
+ }
186
+
187
+ /// Create a label for a span.
188
+ fn label(world: &SystemWorld, span: Span) -> Option<Label<FileId>> {
189
+ Some(Label::primary(span.id()?, world.range(span)?))
190
+ }
191
+
192
+ impl<'a> codespan_reporting::files::Files<'a> for SystemWorld {
193
+ type FileId = FileId;
194
+ type Name = String;
195
+ type Source = Source;
196
+
197
+ fn name(&'a self, id: FileId) -> CodespanResult<Self::Name> {
198
+ let vpath = id.vpath();
199
+ Ok(if let Some(package) = id.package() {
200
+ format!("{package}{}", vpath.as_rooted_path().display())
201
+ } else {
202
+ // Try to express the path relative to the working directory.
203
+ vpath
204
+ .resolve(self.root())
205
+ .and_then(|abs| pathdiff::diff_paths(abs, self.workdir()))
206
+ .as_deref()
207
+ .unwrap_or_else(|| vpath.as_rootless_path())
208
+ .to_string_lossy()
209
+ .into()
210
+ })
211
+ }
212
+
213
+ fn source(&'a self, id: FileId) -> CodespanResult<Self::Source> {
214
+ Ok(self.lookup(id))
215
+ }
216
+
217
+ fn line_index(&'a self, id: FileId, given: usize) -> CodespanResult<usize> {
218
+ let source = self.lookup(id);
219
+ source
220
+ .byte_to_line(given)
221
+ .ok_or_else(|| CodespanError::IndexTooLarge {
222
+ given,
223
+ max: source.len_bytes(),
224
+ })
225
+ }
226
+
227
+ fn line_range(&'a self, id: FileId, given: usize) -> CodespanResult<std::ops::Range<usize>> {
228
+ let source = self.lookup(id);
229
+ source
230
+ .line_to_range(given)
231
+ .ok_or_else(|| CodespanError::LineTooLarge {
232
+ given,
233
+ max: source.len_lines(),
234
+ })
235
+ }
236
+
237
+ fn column_number(&'a self, id: FileId, _: usize, given: usize) -> CodespanResult<usize> {
238
+ let source = self.lookup(id);
239
+ source.byte_to_column(given).ok_or_else(|| {
240
+ let max = source.len_bytes();
241
+ if given <= max {
242
+ CodespanError::InvalidCharBoundary { given }
243
+ } else {
244
+ CodespanError::IndexTooLarge { given, max }
245
+ }
246
+ })
247
+ }
248
+ }
@@ -0,0 +1,19 @@
1
+ use std::fmt::Display;
2
+
3
+ use typst_kit::download::{DownloadState, Downloader, Progress};
4
+
5
+ pub struct SlientDownload<T>(pub T);
6
+
7
+ impl<T: Display> Progress for SlientDownload<T> {
8
+ fn print_start(&mut self) {}
9
+
10
+ fn print_progress(&mut self, _state: &DownloadState) {}
11
+
12
+ fn print_finish(&mut self, _state: &DownloadState) {}
13
+ }
14
+
15
+ /// Returns a new downloader.
16
+ pub fn downloader() -> Downloader {
17
+ let user_agent = concat!("typst-py/", env!("CARGO_PKG_VERSION"));
18
+ Downloader::new(user_agent)
19
+ }
@@ -0,0 +1,86 @@
1
+ use std::cell::OnceCell;
2
+ use std::fs::{self};
3
+ use std::path::PathBuf;
4
+
5
+ use fontdb::{Database, Source};
6
+ use typst::font::{Font, FontBook, FontInfo};
7
+
8
+ /// Searches for fonts.
9
+ pub struct FontSearcher {
10
+ // Metadata about all discovered fonts.
11
+ pub book: FontBook,
12
+ /// Slots that the fonts are loaded into.
13
+ pub fonts: Vec<FontSlot>,
14
+ }
15
+
16
+ /// Holds details about the location of a font and lazily the font itself.
17
+ pub struct FontSlot {
18
+ /// The path at which the font can be found on the system.
19
+ path: PathBuf,
20
+ /// The index of the font in its collection. Zero if the path does not point
21
+ /// to a collection.
22
+ index: u32,
23
+ /// The lazily loaded font.
24
+ font: OnceCell<Option<Font>>,
25
+ }
26
+
27
+ impl FontSlot {
28
+ /// Get the font for this slot.
29
+ pub fn get(&self) -> Option<Font> {
30
+ self.font
31
+ .get_or_init(|| {
32
+ let data = fs::read(&self.path).ok()?.into();
33
+ Font::new(data, self.index)
34
+ })
35
+ .clone()
36
+ }
37
+ }
38
+
39
+ impl FontSearcher {
40
+ /// Create a new, empty system searcher.
41
+ pub fn new() -> Self {
42
+ Self {
43
+ book: FontBook::new(),
44
+ fonts: vec![],
45
+ }
46
+ }
47
+
48
+ /// Search everything that is available.
49
+ pub fn search(&mut self, font_dirs: &[PathBuf], font_files: &[PathBuf]) {
50
+ let mut db = Database::new();
51
+
52
+ // Font paths have highest priority.
53
+ for path in font_dirs {
54
+ db.load_fonts_dir(path);
55
+ }
56
+
57
+ // System fonts have second priority.
58
+ db.load_system_fonts();
59
+
60
+ for path in font_files {
61
+ let _ret = db.load_font_file(path).ok();
62
+ }
63
+
64
+ for face in db.faces() {
65
+ let path = match &face.source {
66
+ Source::File(path) | Source::SharedFile(path, _) => path,
67
+ // We never add binary sources to the database, so there
68
+ // shouln't be any.
69
+ Source::Binary(_) => continue,
70
+ };
71
+
72
+ let info = db
73
+ .with_face_data(face.id, FontInfo::new)
74
+ .expect("database must contain this font");
75
+
76
+ if let Some(info) = info {
77
+ self.book.push(info);
78
+ self.fonts.push(FontSlot {
79
+ path: path.clone(),
80
+ index: face.index,
81
+ font: OnceCell::new(),
82
+ });
83
+ }
84
+ }
85
+ }
86
+ }
@@ -0,0 +1,330 @@
1
+ use std::path::PathBuf;
2
+
3
+ use magnus::{define_module, function, exception, Error }; //, IntoValue};
4
+ use magnus::{prelude::*};
5
+
6
+ use std::collections::HashMap;
7
+ use query::{query as typst_query, QueryCommand, SerializationFormat};
8
+ use typst::foundations::{Dict, Value};
9
+ use typst_library::Feature;
10
+ use world::SystemWorld;
11
+
12
+ mod compiler;
13
+ mod download;
14
+ mod query;
15
+ mod world;
16
+
17
+ fn to_html(
18
+ input: PathBuf,
19
+ root: Option<PathBuf>,
20
+ font_paths: Vec<PathBuf>,
21
+ resource_path: PathBuf,
22
+ ignore_system_fonts: bool,
23
+ sys_inputs: HashMap<String, String>,
24
+ ) -> Result<Vec<Vec<u8>>, Error> {
25
+ let input = input.canonicalize()
26
+ .map_err(|err| magnus::Error::new(exception::arg_error(), err.to_string()))?;
27
+
28
+ let root = if let Some(root) = root {
29
+ root.canonicalize()
30
+ .map_err(|err| magnus::Error::new(exception::arg_error(), err.to_string()))?
31
+ } else if let Some(dir) = input.parent() {
32
+ dir.into()
33
+ } else {
34
+ PathBuf::new()
35
+ };
36
+
37
+ let resource_path = resource_path.canonicalize()
38
+ .map_err(|err| magnus::Error::new(exception::arg_error(), err.to_string()))?;
39
+
40
+ let mut default_fonts = Vec::new();
41
+ for entry in walkdir::WalkDir::new(resource_path.join("fonts")) {
42
+ let path = entry
43
+ .map_err(|err| magnus::Error::new(exception::arg_error(), err.to_string()))?
44
+ .into_path();
45
+ let Some(extension) = path.extension() else {
46
+ continue;
47
+ };
48
+ if extension == "ttf" || extension == "otf" {
49
+ default_fonts.push(path);
50
+ }
51
+ }
52
+
53
+ let mut features = Vec::new();
54
+ features.push(Feature::Html);
55
+
56
+ let feat = features.iter()
57
+ .map(|&feature|
58
+ match feature {
59
+ Feature::Html => typst::Feature::Html,
60
+ _ => typst::Feature::Html // TODO: fix this hack
61
+ }
62
+ )
63
+ .collect();
64
+
65
+ let mut world = SystemWorld::builder(root, input)
66
+ .inputs(Dict::from_iter(
67
+ sys_inputs
68
+ .into_iter()
69
+ .map(|(k, v)| (k.into(), Value::Str(v.into()))),
70
+ ))
71
+ .features(feat)
72
+ .font_paths(font_paths)
73
+ .ignore_system_fonts(ignore_system_fonts)
74
+ .build()
75
+ .map_err(|msg| magnus::Error::new(exception::arg_error(), msg.to_string()))?;
76
+
77
+ let bytes = world
78
+ .compile(Some("html"), None, &Vec::new())
79
+ .map_err(|msg| magnus::Error::new(exception::arg_error(), msg.to_string()))?;
80
+
81
+ Ok(bytes)
82
+ }
83
+
84
+ fn to_svg(
85
+ input: PathBuf,
86
+ root: Option<PathBuf>,
87
+ font_paths: Vec<PathBuf>,
88
+ resource_path: PathBuf,
89
+ ignore_system_fonts: bool,
90
+ sys_inputs: HashMap<String, String>,
91
+ ) -> Result<Vec<Vec<u8>>, Error> {
92
+ let input = input.canonicalize()
93
+ .map_err(|err| magnus::Error::new(exception::arg_error(), err.to_string()))?;
94
+
95
+ let root = if let Some(root) = root {
96
+ root.canonicalize()
97
+ .map_err(|err| magnus::Error::new(exception::arg_error(), err.to_string()))?
98
+ } else if let Some(dir) = input.parent() {
99
+ dir.into()
100
+ } else {
101
+ PathBuf::new()
102
+ };
103
+
104
+ let resource_path = resource_path.canonicalize()
105
+ .map_err(|err| magnus::Error::new(exception::arg_error(), err.to_string()))?;
106
+
107
+ let mut default_fonts = Vec::new();
108
+ for entry in walkdir::WalkDir::new(resource_path.join("fonts")) {
109
+ let path = entry
110
+ .map_err(|err| magnus::Error::new(exception::arg_error(), err.to_string()))?
111
+ .into_path();
112
+ let Some(extension) = path.extension() else {
113
+ continue;
114
+ };
115
+ if extension == "ttf" || extension == "otf" {
116
+ default_fonts.push(path);
117
+ }
118
+ }
119
+
120
+ let mut world = SystemWorld::builder(root, input)
121
+ .inputs(Dict::from_iter(
122
+ sys_inputs
123
+ .into_iter()
124
+ .map(|(k, v)| (k.into(), Value::Str(v.into()))),
125
+ ))
126
+ .font_paths(font_paths)
127
+ .ignore_system_fonts(ignore_system_fonts)
128
+ .build()
129
+ .map_err(|msg| magnus::Error::new(exception::arg_error(), msg.to_string()))?;
130
+
131
+ let svg_bytes = world
132
+ .compile(Some("svg"), None, &Vec::new())
133
+ .map_err(|msg| magnus::Error::new(exception::arg_error(), msg.to_string()))?;
134
+
135
+ Ok(svg_bytes)
136
+ }
137
+
138
+ fn to_png(
139
+ input: PathBuf,
140
+ root: Option<PathBuf>,
141
+ font_paths: Vec<PathBuf>,
142
+ resource_path: PathBuf,
143
+ ignore_system_fonts: bool,
144
+ sys_inputs: HashMap<String, String>,
145
+ ) -> Result<Vec<Vec<u8>>, Error> {
146
+ let input = input.canonicalize()
147
+ .map_err(|err| magnus::Error::new(exception::arg_error(), err.to_string()))?;
148
+
149
+ let root = if let Some(root) = root {
150
+ root.canonicalize()
151
+ .map_err(|err| magnus::Error::new(exception::arg_error(), err.to_string()))?
152
+ } else if let Some(dir) = input.parent() {
153
+ dir.into()
154
+ } else {
155
+ PathBuf::new()
156
+ };
157
+
158
+ let resource_path = resource_path.canonicalize()
159
+ .map_err(|err| magnus::Error::new(exception::arg_error(), err.to_string()))?;
160
+
161
+ let mut default_fonts = Vec::new();
162
+ for entry in walkdir::WalkDir::new(resource_path.join("fonts")) {
163
+ let path = entry
164
+ .map_err(|err| magnus::Error::new(exception::arg_error(), err.to_string()))?
165
+ .into_path();
166
+ let Some(extension) = path.extension() else {
167
+ continue;
168
+ };
169
+ if extension == "ttf" || extension == "otf" {
170
+ default_fonts.push(path);
171
+ }
172
+ }
173
+
174
+ let mut world = SystemWorld::builder(root, input)
175
+ .inputs(Dict::from_iter(
176
+ sys_inputs
177
+ .into_iter()
178
+ .map(|(k, v)| (k.into(), Value::Str(v.into()))),
179
+ ))
180
+ .font_paths(font_paths)
181
+ .ignore_system_fonts(ignore_system_fonts)
182
+ .build()
183
+ .map_err(|msg| magnus::Error::new(exception::arg_error(), msg.to_string()))?;
184
+
185
+ let bytes = world
186
+ .compile(Some("png"), None, &Vec::new())
187
+ .map_err(|msg| magnus::Error::new(exception::arg_error(), msg.to_string()))?;
188
+
189
+ Ok(bytes)
190
+ }
191
+
192
+ fn to_pdf(
193
+ input: PathBuf,
194
+ root: Option<PathBuf>,
195
+ font_paths: Vec<PathBuf>,
196
+ resource_path: PathBuf,
197
+ ignore_system_fonts: bool,
198
+ sys_inputs: HashMap<String, String>,
199
+ ) -> Result<Vec<Vec<u8>>, Error> {
200
+ let input = input.canonicalize()
201
+ .map_err(|err| magnus::Error::new(exception::arg_error(), err.to_string()))?;
202
+
203
+ let root = if let Some(root) = root {
204
+ root.canonicalize()
205
+ .map_err(|err| magnus::Error::new(exception::arg_error(), err.to_string()))?
206
+ } else if let Some(dir) = input.parent() {
207
+ dir.into()
208
+ } else {
209
+ PathBuf::new()
210
+ };
211
+
212
+ let resource_path = resource_path.canonicalize()
213
+ .map_err(|err| magnus::Error::new(exception::arg_error(), err.to_string()))?;
214
+
215
+ let mut default_fonts = Vec::new();
216
+ for entry in walkdir::WalkDir::new(resource_path.join("fonts")) {
217
+ let path = entry
218
+ .map_err(|err| magnus::Error::new(exception::arg_error(), err.to_string()))?
219
+ .into_path();
220
+ let Some(extension) = path.extension() else {
221
+ continue;
222
+ };
223
+ if extension == "ttf" || extension == "otf" {
224
+ default_fonts.push(path);
225
+ }
226
+ }
227
+
228
+ let mut world = SystemWorld::builder(root, input)
229
+ .inputs(Dict::from_iter(
230
+ sys_inputs
231
+ .into_iter()
232
+ .map(|(k, v)| (k.into(), Value::Str(v.into()))),
233
+ ))
234
+ .font_paths(font_paths)
235
+ .ignore_system_fonts(ignore_system_fonts)
236
+ .build()
237
+ .map_err(|msg| magnus::Error::new(exception::arg_error(), msg.to_string()))?;
238
+
239
+ let pdf_bytes = world
240
+ .compile(Some("pdf"), None, &Vec::new())
241
+ .map_err(|msg| magnus::Error::new(exception::arg_error(), msg.to_string()))?;
242
+
243
+ Ok(pdf_bytes)
244
+ }
245
+
246
+ fn query(
247
+ selector: String,
248
+ field: Option<String>,
249
+ one: bool,
250
+ format: Option<String>,
251
+ input: PathBuf,
252
+ root: Option<PathBuf>,
253
+ font_paths: Vec<PathBuf>,
254
+ resource_path: PathBuf,
255
+ ignore_system_fonts: bool,
256
+ sys_inputs: HashMap<String, String>,
257
+ ) -> Result<String, Error> {
258
+ let format = match format.unwrap().to_ascii_lowercase().as_str() {
259
+ "json" => SerializationFormat::Json,
260
+ "yaml" => SerializationFormat::Yaml,
261
+ _ => return Err(magnus::Error::new(exception::arg_error(), "unsupported serialization format"))?,
262
+ };
263
+
264
+ let input = input.canonicalize()
265
+ .map_err(|err| magnus::Error::new(exception::arg_error(), err.to_string()))?;
266
+
267
+ let root = if let Some(root) = root {
268
+ root.canonicalize()
269
+ .map_err(|err| magnus::Error::new(exception::arg_error(), err.to_string()))?
270
+ } else if let Some(dir) = input.parent() {
271
+ dir.into()
272
+ } else {
273
+ PathBuf::new()
274
+ };
275
+
276
+ let resource_path = resource_path.canonicalize()
277
+ .map_err(|err| magnus::Error::new(exception::arg_error(), err.to_string()))?;
278
+
279
+ let mut default_fonts = Vec::new();
280
+ for entry in walkdir::WalkDir::new(resource_path.join("fonts")) {
281
+ let path = entry
282
+ .map_err(|err| magnus::Error::new(exception::arg_error(), err.to_string()))?
283
+ .into_path();
284
+ let Some(extension) = path.extension() else {
285
+ continue;
286
+ };
287
+ if extension == "ttf" || extension == "otf" {
288
+ default_fonts.push(path);
289
+ }
290
+ }
291
+
292
+ let mut world = SystemWorld::builder(root, input)
293
+ .inputs(Dict::from_iter(
294
+ sys_inputs
295
+ .into_iter()
296
+ .map(|(k, v)| (k.into(), Value::Str(v.into()))),
297
+ ))
298
+ .font_paths(font_paths)
299
+ .ignore_system_fonts(ignore_system_fonts)
300
+ .build()
301
+ .map_err(|msg| magnus::Error::new(exception::arg_error(), msg.to_string()))?;
302
+
303
+ let result = typst_query(
304
+ &mut world,
305
+ &QueryCommand {
306
+ selector: selector.into(),
307
+ field: field.map(Into::into),
308
+ one,
309
+ format,
310
+ },
311
+ );
312
+
313
+ match result {
314
+ Ok(data) => Ok(data),
315
+ Err(msg) => Err(magnus::Error::new(exception::arg_error(), msg.to_string())),
316
+ }
317
+ }
318
+
319
+ #[magnus::init]
320
+ fn init() -> Result<(), Error> {
321
+ env_logger::init();
322
+
323
+ let module = define_module("Typst")?;
324
+ module.define_singleton_method("_to_pdf", function!(to_pdf, 6))?;
325
+ module.define_singleton_method("_to_svg", function!(to_svg, 6))?;
326
+ module.define_singleton_method("_to_png", function!(to_png, 6))?;
327
+ module.define_singleton_method("_to_html", function!(to_html, 6))?;
328
+ module.define_singleton_method("_query", function!(query, 10))?;
329
+ Ok(())
330
+ }