typst 0.0.5 → 0.13.1
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.
- checksums.yaml +4 -4
- data/README.md +38 -4
- data/README.typ +38 -4
- data/ext/typst/Cargo.toml +30 -15
- data/ext/typst/src/compiler-new.rs +771 -0
- data/ext/typst/src/compiler.rs +100 -39
- data/ext/typst/src/download.rs +11 -71
- data/ext/typst/src/lib.rs +218 -21
- data/ext/typst/src/query.rs +131 -0
- data/ext/typst/src/world.rs +130 -136
- data/lib/typst.rb +61 -2
- metadata +19 -9
- data/ext/typst/Cargo.lock +0 -2840
- data/ext/typst/src/lib.wip.rs +0 -203
data/ext/typst/src/compiler.rs
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
use chrono::{Datelike, Timelike};
|
2
2
|
use codespan_reporting::diagnostic::{Diagnostic, Label};
|
3
3
|
use codespan_reporting::term::{self, termcolor};
|
4
|
-
use
|
5
|
-
use typst::
|
6
|
-
use typst::
|
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;
|
7
9
|
use typst::syntax::{FileId, Source, Span};
|
8
10
|
use typst::{World, WorldExt};
|
9
11
|
|
@@ -13,55 +15,86 @@ type CodespanResult<T> = Result<T, CodespanError>;
|
|
13
15
|
type CodespanError = codespan_reporting::files::Error;
|
14
16
|
|
15
17
|
impl SystemWorld {
|
16
|
-
pub fn
|
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>>> {
|
17
24
|
// Reset everything and ensure that the main file is present.
|
18
25
|
self.reset();
|
19
26
|
self.source(self.main()).map_err(|err| err.to_string())?;
|
20
27
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
+
}
|
45
58
|
}
|
46
59
|
}
|
47
60
|
}
|
48
61
|
|
49
62
|
/// Export to a PDF.
|
50
63
|
#[inline]
|
51
|
-
fn
|
52
|
-
|
53
|
-
|
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();
|
54
74
|
Ok(buffer)
|
55
75
|
}
|
56
76
|
|
57
|
-
/// Export to
|
77
|
+
/// Export to a PDF.
|
58
78
|
#[inline]
|
59
|
-
fn
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
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
|
+
})?;
|
65
98
|
Ok(buffer)
|
66
99
|
}
|
67
100
|
|
@@ -78,6 +111,34 @@ fn now() -> Option<Datetime> {
|
|
78
111
|
)
|
79
112
|
}
|
80
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
|
+
|
81
142
|
/// Format diagnostic messages.\
|
82
143
|
pub fn format_diagnostics(
|
83
144
|
world: &SystemWorld,
|
@@ -141,7 +202,7 @@ impl<'a> codespan_reporting::files::Files<'a> for SystemWorld {
|
|
141
202
|
// Try to express the path relative to the working directory.
|
142
203
|
vpath
|
143
204
|
.resolve(self.root())
|
144
|
-
.and_then(|abs| pathdiff::diff_paths(
|
205
|
+
.and_then(|abs| pathdiff::diff_paths(abs, self.workdir()))
|
145
206
|
.as_deref()
|
146
207
|
.unwrap_or_else(|| vpath.as_rootless_path())
|
147
208
|
.to_string_lossy()
|
data/ext/typst/src/download.rs
CHANGED
@@ -1,79 +1,19 @@
|
|
1
|
-
use std::
|
1
|
+
use std::fmt::Display;
|
2
2
|
|
3
|
-
use
|
3
|
+
use typst_kit::download::{DownloadState, Downloader, Progress};
|
4
4
|
|
5
|
-
|
6
|
-
#[allow(clippy::result_large_err)]
|
7
|
-
pub fn download(url: &str) -> Result<Vec<u8>, ureq::Error> {
|
8
|
-
let mut builder =
|
9
|
-
ureq::AgentBuilder::new().user_agent(concat!("typst/{}", env!("CARGO_PKG_VERSION")));
|
5
|
+
pub struct SlientDownload<T>(pub T);
|
10
6
|
|
11
|
-
|
12
|
-
|
13
|
-
.to_url()
|
14
|
-
.and_then(|url| ureq::Proxy::new(url).ok())
|
15
|
-
{
|
16
|
-
builder = builder.proxy(proxy);
|
17
|
-
}
|
7
|
+
impl<T: Display> Progress for SlientDownload<T> {
|
8
|
+
fn print_start(&mut self) {}
|
18
9
|
|
19
|
-
|
20
|
-
let response = agent.get(url).call()?;
|
21
|
-
Ok(RemoteReader::from_response(response).download()?)
|
22
|
-
}
|
10
|
+
fn print_progress(&mut self, _state: &DownloadState) {}
|
23
11
|
|
24
|
-
|
25
|
-
/// over a websocket and displays statistics about its progress.
|
26
|
-
///
|
27
|
-
/// Downloads will _never_ fail due to statistics failing to print, print errors
|
28
|
-
/// are silently ignored.
|
29
|
-
struct RemoteReader {
|
30
|
-
reader: Box<dyn Read + Send + Sync + 'static>,
|
31
|
-
content_len: Option<usize>,
|
12
|
+
fn print_finish(&mut self, _state: &DownloadState) {}
|
32
13
|
}
|
33
14
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
/// optimization, if present.
|
39
|
-
pub fn from_response(response: Response) -> Self {
|
40
|
-
let content_len: Option<usize> = response
|
41
|
-
.header("Content-Length")
|
42
|
-
.and_then(|header| header.parse().ok());
|
43
|
-
|
44
|
-
Self {
|
45
|
-
reader: response.into_reader(),
|
46
|
-
content_len,
|
47
|
-
}
|
48
|
-
}
|
49
|
-
|
50
|
-
/// Download the bodies content as raw bytes while attempting to print
|
51
|
-
/// download statistics to standard error. Download progress gets displayed
|
52
|
-
/// and updated every second.
|
53
|
-
///
|
54
|
-
/// These statistics will never prevent a download from completing, errors
|
55
|
-
/// are silently ignored.
|
56
|
-
pub fn download(mut self) -> io::Result<Vec<u8>> {
|
57
|
-
let mut buffer = vec![0; 8192];
|
58
|
-
let mut data = match self.content_len {
|
59
|
-
Some(content_len) => Vec::with_capacity(content_len),
|
60
|
-
None => Vec::with_capacity(8192),
|
61
|
-
};
|
62
|
-
|
63
|
-
loop {
|
64
|
-
let read = match self.reader.read(&mut buffer) {
|
65
|
-
Ok(0) => break,
|
66
|
-
Ok(n) => n,
|
67
|
-
// If the data is not yet ready but will be available eventually
|
68
|
-
// keep trying until we either get an actual error, receive data
|
69
|
-
// or an Ok(0).
|
70
|
-
Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
|
71
|
-
Err(e) => return Err(e),
|
72
|
-
};
|
73
|
-
|
74
|
-
data.extend(&buffer[..read]);
|
75
|
-
}
|
76
|
-
|
77
|
-
Ok(data)
|
78
|
-
}
|
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)
|
79
19
|
}
|
data/ext/typst/src/lib.rs
CHANGED
@@ -1,22 +1,94 @@
|
|
1
1
|
use std::path::PathBuf;
|
2
2
|
|
3
|
-
use magnus::{define_module, function, exception, Error
|
3
|
+
use magnus::{define_module, function, exception, Error }; //, IntoValue};
|
4
4
|
use magnus::{prelude::*};
|
5
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;
|
6
10
|
use world::SystemWorld;
|
7
11
|
|
8
12
|
mod compiler;
|
9
13
|
mod download;
|
10
|
-
mod
|
11
|
-
mod package;
|
14
|
+
mod query;
|
12
15
|
mod world;
|
13
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
|
+
|
14
84
|
fn to_svg(
|
15
85
|
input: PathBuf,
|
16
86
|
root: Option<PathBuf>,
|
17
87
|
font_paths: Vec<PathBuf>,
|
18
88
|
resource_path: PathBuf,
|
19
|
-
|
89
|
+
ignore_system_fonts: bool,
|
90
|
+
sys_inputs: HashMap<String, String>,
|
91
|
+
) -> Result<Vec<Vec<u8>>, Error> {
|
20
92
|
let input = input.canonicalize()
|
21
93
|
.map_err(|err| magnus::Error::new(exception::arg_error(), err.to_string()))?;
|
22
94
|
|
@@ -46,24 +118,85 @@ fn to_svg(
|
|
46
118
|
}
|
47
119
|
|
48
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
|
+
))
|
49
126
|
.font_paths(font_paths)
|
50
|
-
.
|
127
|
+
.ignore_system_fonts(ignore_system_fonts)
|
51
128
|
.build()
|
52
129
|
.map_err(|msg| magnus::Error::new(exception::arg_error(), msg.to_string()))?;
|
53
130
|
|
54
131
|
let svg_bytes = world
|
55
|
-
.
|
132
|
+
.compile(Some("svg"), None, &Vec::new())
|
56
133
|
.map_err(|msg| magnus::Error::new(exception::arg_error(), msg.to_string()))?;
|
57
134
|
|
58
135
|
Ok(svg_bytes)
|
59
136
|
}
|
60
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
|
+
|
61
192
|
fn to_pdf(
|
62
193
|
input: PathBuf,
|
63
194
|
root: Option<PathBuf>,
|
64
195
|
font_paths: Vec<PathBuf>,
|
65
196
|
resource_path: PathBuf,
|
66
|
-
|
197
|
+
ignore_system_fonts: bool,
|
198
|
+
sys_inputs: HashMap<String, String>,
|
199
|
+
) -> Result<Vec<Vec<u8>>, Error> {
|
67
200
|
let input = input.canonicalize()
|
68
201
|
.map_err(|err| magnus::Error::new(exception::arg_error(), err.to_string()))?;
|
69
202
|
|
@@ -93,41 +226,105 @@ fn to_pdf(
|
|
93
226
|
}
|
94
227
|
|
95
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
|
+
))
|
96
234
|
.font_paths(font_paths)
|
97
|
-
.
|
235
|
+
.ignore_system_fonts(ignore_system_fonts)
|
98
236
|
.build()
|
99
237
|
.map_err(|msg| magnus::Error::new(exception::arg_error(), msg.to_string()))?;
|
100
238
|
|
101
239
|
let pdf_bytes = world
|
102
|
-
.
|
240
|
+
.compile(Some("pdf"), None, &Vec::new())
|
103
241
|
.map_err(|msg| magnus::Error::new(exception::arg_error(), msg.to_string()))?;
|
104
242
|
|
105
243
|
Ok(pdf_bytes)
|
106
244
|
}
|
107
245
|
|
108
|
-
fn
|
246
|
+
fn query(
|
247
|
+
selector: String,
|
248
|
+
field: Option<String>,
|
249
|
+
one: bool,
|
250
|
+
format: Option<String>,
|
109
251
|
input: PathBuf,
|
110
|
-
output: PathBuf,
|
111
252
|
root: Option<PathBuf>,
|
112
253
|
font_paths: Vec<PathBuf>,
|
113
254
|
resource_path: PathBuf,
|
114
|
-
|
115
|
-
|
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
|
+
};
|
116
263
|
|
117
|
-
|
118
|
-
.map_err(|
|
264
|
+
let input = input.canonicalize()
|
265
|
+
.map_err(|err| magnus::Error::new(exception::arg_error(), err.to_string()))?;
|
119
266
|
|
120
|
-
let
|
121
|
-
|
122
|
-
|
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
|
+
}
|
123
318
|
|
124
319
|
#[magnus::init]
|
125
320
|
fn init() -> Result<(), Error> {
|
126
321
|
env_logger::init();
|
127
322
|
|
128
323
|
let module = define_module("Typst")?;
|
129
|
-
module.define_singleton_method("_to_pdf", function!(to_pdf,
|
130
|
-
module.define_singleton_method("
|
131
|
-
module.define_singleton_method("
|
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))?;
|
132
329
|
Ok(())
|
133
330
|
}
|