typst 0.14.2.3 → 0.15.0.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bdc56381647f2be411648ebbf5525576c5c6a259257cafd29de225dbd50da25c
4
- data.tar.gz: 414023a808e3081a46e5e3ca64aa44920aedc41bbeca7c7004557e2a76b682f9
3
+ metadata.gz: 0a438a6f795f22c5957eeaefbde3b825e7bf8e8634d75bc402de95321bc0e28d
4
+ data.tar.gz: 192a0dafec2d0b226674eabda64cc71926cc491444f2c2f391a868936c68f044
5
5
  SHA512:
6
- metadata.gz: bf751aa8cfe5c0cb9f7433728f61ef1882af9fdea3b0aa031fa87e906a6be1880b1832a74597871911751fc912fb93f2f8946d4b8fd022c175828865d428938c
7
- data.tar.gz: 62d507c32cdd32be9cf125350953a57971968e5aff1b43604d506e640901a327b940ab51c5becb6e4890ce1c8f656c4adb0a8bae19e1b3b217a6d82c8cddfb80
6
+ metadata.gz: 2df9d3457d9e2293132e1b528c646c74f00974cc27cb1d3d35801fc8989ee9d9c5fe1b83a615cc245a8ca3f04349f356d385d1dd653b6dba8d4d5ac77ab285ee
7
+ data.tar.gz: 447e18da8b0678394bcde2f5ef6fe25235e8d4d3c9e484375944a0f1054786739dec77bcce2291a252360dc284d40062dcac4dcd2de94e73fa4f190741887401
data/ext/typst/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "typst"
3
- version = "0.14.2"
3
+ version = "0.15.0"
4
4
  edition = "2021"
5
5
 
6
6
  [lib]
@@ -12,7 +12,7 @@ chrono = { version = "0.4.42", default-features = false, features = [
12
12
  "std",
13
13
  ] }
14
14
  codespan-reporting = "0.13"
15
- comemo = "0.5.0"
15
+ comemo = "0.5.1"
16
16
  dirs = "5" #
17
17
  ecow = "0.2"
18
18
  env_logger = "0.10.1" #
@@ -29,18 +29,21 @@ tar = "0.4" #
29
29
  serde = { version = "1.0.228", features = ["derive"] }
30
30
  serde_json = "1"
31
31
  serde_yaml = "0.9"
32
- typst = "0.14.2"
33
- typst-library = "0.14.2"
34
- typst-kit = { version = "0.14.2", features = [
35
- "downloads",
36
- "embed-fonts",
32
+ typst = "0.15.0"
33
+ typst-library = "0.15.0"
34
+ typst-kit = { version = "0.15.0", features = [
35
+ "embedded-fonts",
36
+ "scan-fonts",
37
+ "system-downloader",
38
+ "system-packages",
37
39
  "vendor-openssl",
38
40
  ] }
39
- typst-pdf = "0.14.2"
40
- typst-svg = "0.14.2"
41
- typst-html = "0.14.2"
42
- typst-render = "0.14.2"
43
- typst-eval = "0.14.2"
41
+ typst-pdf = "0.15.0"
42
+ typst-svg = "0.15.0"
43
+ typst-html = "0.15.0"
44
+ typst-render = "0.15.0"
45
+ typst-eval = "0.15.0"
46
+ typst-layout = "0.15.0"
44
47
  ureq = { version = "2", default-features = false, features = [
45
48
  "gzip",
46
49
  "socks-proxy",
@@ -49,4 +52,4 @@ walkdir = "2.4.0"
49
52
 
50
53
  # enable rb-sys feature to test against Ruby head. This is only needed if you
51
54
  # want to work with the unreleased, in-development, next version of Ruby
52
- rb-sys = { version = "0.9.124", default-features = false, features = ["stable-api-compiled-fallback"] }
55
+ rb-sys = { version = "0.9.128", default-features = false, features = ["stable-api-compiled-fallback"] }
@@ -2,11 +2,11 @@ use chrono::{Datelike, Timelike};
2
2
  use codespan_reporting::diagnostic::{Diagnostic, Label};
3
3
  use codespan_reporting::term::{self, termcolor};
4
4
  use ecow::{eco_format, EcoString};
5
- use typst::diag::{Severity, SourceDiagnostic, StrResult, Warned};
5
+ use typst::diag::{At, Severity, SourceDiagnostic, StrResult, Warned};
6
6
  use typst::foundations::Datetime;
7
7
  use typst_html::HtmlDocument;
8
- use typst::layout::PagedDocument;
9
- use typst::syntax::{FileId, Lines, Span};
8
+ use typst_layout::PagedDocument;
9
+ use typst::syntax::{DiagSpan, FileId, Lines, Span};
10
10
  use typst::{World, WorldExt};
11
11
 
12
12
  use crate::world::SystemWorld;
@@ -45,7 +45,9 @@ impl SystemWorld {
45
45
  "pdf" => Ok(vec![export_pdf(
46
46
  &document,
47
47
  self,
48
- typst_pdf::PdfStandards::new(pdf_standards)?,
48
+ typst_pdf::PdfStandards::new(pdf_standards)
49
+ .map_err(|e| eco_format!("PDF standards error: {:?}", e))
50
+ .at(Span::detached()).unwrap()
49
51
  )?]),
50
52
  "png" => Ok(export_image(&document, ImageExportFormat::Png, ppi)?),
51
53
  "svg" => Ok(export_image(&document, ImageExportFormat::Svg, ppi)?),
@@ -65,7 +67,7 @@ fn export_html(
65
67
  document: &HtmlDocument,
66
68
  world: &SystemWorld,
67
69
  ) -> StrResult<Vec<u8>> {
68
- let html = typst_html::html(document)
70
+ let html = typst_html::html(document, &typst_html::HtmlOptions::default())
69
71
  .map_err(|e| match format_diagnostics(world, &e, &[]) {
70
72
  Ok(e) => EcoString::from(e),
71
73
  Err(err) => eco_format!("failed to print diagnostics ({err})"),
@@ -81,11 +83,10 @@ fn export_pdf(
81
83
  world: &SystemWorld,
82
84
  standards: typst_pdf::PdfStandards,
83
85
  ) -> StrResult<Vec<u8>> {
84
- let ident = world.input().to_string_lossy();
85
86
  let buffer = typst_pdf::pdf(
86
87
  document,
87
88
  &typst_pdf::PdfOptions {
88
- ident: typst::foundations::Smart::Custom(&ident),
89
+ ident: typst::foundations::Smart::Auto,
89
90
  timestamp: now().map(typst_pdf::Timestamp::new_utc),
90
91
  standards,
91
92
  ..Default::default()
@@ -95,7 +96,7 @@ fn export_pdf(
95
96
  Ok(e) => EcoString::from(e),
96
97
  Err(err) => eco_format!("failed to print diagnostics ({err})"),
97
98
  })?;
98
- Ok(buffer)
99
+ Ok(buffer.into())
99
100
  }
100
101
 
101
102
  /// Get the current date and time in UTC.
@@ -124,13 +125,19 @@ fn export_image(
124
125
  ppi: Option<f32>,
125
126
  ) -> StrResult<Vec<Vec<u8>>> {
126
127
  let mut buffers = Vec::new();
127
- for page in &document.pages {
128
+ for page in document.pages() {
128
129
  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})"))?,
130
+ ImageExportFormat::Png => typst_render::render(
131
+ page,
132
+ &typst_render::RenderOptions {
133
+ pixel_per_pt: typst::utils::Scalar::new(f64::from(ppi.unwrap_or(144.0) / 72.0)),
134
+ render_bleed: false,
135
+ },
136
+ )
137
+ .encode_png()
138
+ .map_err(|err| eco_format!("failed to write PNG file ({err})"))?,
132
139
  ImageExportFormat::Svg => {
133
- let svg = typst_svg::svg(page);
140
+ let svg = typst_svg::svg(page, &typst_svg::SvgOptions::default());
134
141
  svg.as_bytes().to_vec()
135
142
  }
136
143
  };
@@ -162,7 +169,7 @@ pub fn format_diagnostics(
162
169
  diagnostic
163
170
  .hints
164
171
  .iter()
165
- .map(|e| (eco_format!("hint: {e}")).into())
172
+ .map(|e| (eco_format!("hint: {}", e.v)).into())
166
173
  .collect(),
167
174
  )
168
175
  .with_labels(label(world, diagnostic.span).into_iter().collect());
@@ -185,7 +192,8 @@ pub fn format_diagnostics(
185
192
  }
186
193
 
187
194
  /// Create a label for a span.
188
- fn label(world: &SystemWorld, span: Span) -> Option<Label<FileId>> {
195
+ fn label(world: &SystemWorld, span: impl Into<DiagSpan>) -> Option<Label<FileId>> {
196
+ let span = span.into();
189
197
  Some(Label::primary(span.id()?, world.range(span)?))
190
198
  }
191
199
 
@@ -196,15 +204,16 @@ impl<'a> codespan_reporting::files::Files<'a> for SystemWorld {
196
204
 
197
205
  fn name(&'a self, id: FileId) -> CodespanResult<Self::Name> {
198
206
  let vpath = id.vpath();
199
- Ok(if let Some(package) = id.package() {
200
- format!("{package}{}", vpath.as_rooted_path().display())
207
+ Ok(if let typst::syntax::VirtualRoot::Package(package) = id.root() {
208
+ format!("{package}{}", vpath.get_with_slash())
201
209
  } else {
202
210
  // Try to express the path relative to the working directory.
203
211
  vpath
204
- .resolve(self.root())
212
+ .realize(self.root())
213
+ .ok()
205
214
  .and_then(|abs| pathdiff::diff_paths(abs, self.workdir()))
206
215
  .as_deref()
207
- .unwrap_or_else(|| vpath.as_rootless_path())
216
+ .unwrap_or_else(|| std::path::Path::new(vpath.get_without_slash()))
208
217
  .to_string_lossy()
209
218
  .into()
210
219
  })
@@ -1,19 +1,7 @@
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
- }
1
+ use typst_kit::downloader::{Downloader, SystemDownloader};
14
2
 
15
3
  /// Returns a new downloader.
16
- pub fn downloader() -> Downloader {
4
+ pub fn downloader() -> impl Downloader {
17
5
  let user_agent = concat!("typst-rb/", env!("CARGO_PKG_VERSION"));
18
- Downloader::new(user_agent)
6
+ SystemDownloader::new(user_agent)
19
7
  }
data/ext/typst/src/lib.rs CHANGED
@@ -19,8 +19,8 @@ fn to_html(
19
19
  input: PathBuf,
20
20
  root: Option<PathBuf>,
21
21
  font_paths: Vec<PathBuf>,
22
- resource_path: PathBuf,
23
22
  ignore_system_fonts: bool,
23
+ ignore_embedded_fonts: bool,
24
24
  sys_inputs: HashMap<String, String>,
25
25
  ) -> Result<Vec<Vec<u8>>, Error> {
26
26
  let input = input.canonicalize()
@@ -35,22 +35,6 @@ fn to_html(
35
35
  PathBuf::new()
36
36
  };
37
37
 
38
- let resource_path = resource_path.canonicalize()
39
- .map_err(|err| magnus::Error::new(ruby.exception_arg_error(), err.to_string()))?;
40
-
41
- let mut default_fonts = Vec::new();
42
- for entry in walkdir::WalkDir::new(resource_path.join("fonts")) {
43
- let path = entry
44
- .map_err(|err| magnus::Error::new(ruby.exception_arg_error(), err.to_string()))?
45
- .into_path();
46
- let Some(extension) = path.extension() else {
47
- continue;
48
- };
49
- if extension == "ttf" || extension == "otf" {
50
- default_fonts.push(path);
51
- }
52
- }
53
-
54
38
  let mut features = Vec::new();
55
39
  features.push(Feature::Html);
56
40
 
@@ -72,6 +56,7 @@ fn to_html(
72
56
  .features(feat)
73
57
  .font_paths(font_paths)
74
58
  .ignore_system_fonts(ignore_system_fonts)
59
+ .ignore_embedded_fonts(ignore_embedded_fonts)
75
60
  .build()
76
61
  .map_err(|msg| magnus::Error::new(ruby.exception_arg_error(), msg.to_string()))?;
77
62
 
@@ -87,8 +72,8 @@ fn to_svg(
87
72
  input: PathBuf,
88
73
  root: Option<PathBuf>,
89
74
  font_paths: Vec<PathBuf>,
90
- resource_path: PathBuf,
91
75
  ignore_system_fonts: bool,
76
+ ignore_embedded_fonts: bool,
92
77
  sys_inputs: HashMap<String, String>,
93
78
  ) -> Result<Vec<Vec<u8>>, Error> {
94
79
  let input = input.canonicalize()
@@ -103,22 +88,6 @@ fn to_svg(
103
88
  PathBuf::new()
104
89
  };
105
90
 
106
- let resource_path = resource_path.canonicalize()
107
- .map_err(|err| magnus::Error::new(ruby.exception_arg_error(), err.to_string()))?;
108
-
109
- let mut default_fonts = Vec::new();
110
- for entry in walkdir::WalkDir::new(resource_path.join("fonts")) {
111
- let path = entry
112
- .map_err(|err| magnus::Error::new(ruby.exception_arg_error(), err.to_string()))?
113
- .into_path();
114
- let Some(extension) = path.extension() else {
115
- continue;
116
- };
117
- if extension == "ttf" || extension == "otf" {
118
- default_fonts.push(path);
119
- }
120
- }
121
-
122
91
  let mut world = SystemWorld::builder(root, input)
123
92
  .inputs(Dict::from_iter(
124
93
  sys_inputs
@@ -127,6 +96,7 @@ fn to_svg(
127
96
  ))
128
97
  .font_paths(font_paths)
129
98
  .ignore_system_fonts(ignore_system_fonts)
99
+ .ignore_embedded_fonts(ignore_embedded_fonts)
130
100
  .build()
131
101
  .map_err(|msg| magnus::Error::new(ruby.exception_arg_error(), msg.to_string()))?;
132
102
 
@@ -142,8 +112,8 @@ fn to_png(
142
112
  input: PathBuf,
143
113
  root: Option<PathBuf>,
144
114
  font_paths: Vec<PathBuf>,
145
- resource_path: PathBuf,
146
115
  ignore_system_fonts: bool,
116
+ ignore_embedded_fonts: bool,
147
117
  sys_inputs: HashMap<String, String>,
148
118
  ppi: Option<f32>,
149
119
  ) -> Result<Vec<Vec<u8>>, Error> {
@@ -159,22 +129,6 @@ fn to_png(
159
129
  PathBuf::new()
160
130
  };
161
131
 
162
- let resource_path = resource_path.canonicalize()
163
- .map_err(|err| magnus::Error::new(ruby.exception_arg_error(), err.to_string()))?;
164
-
165
- let mut default_fonts = Vec::new();
166
- for entry in walkdir::WalkDir::new(resource_path.join("fonts")) {
167
- let path = entry
168
- .map_err(|err| magnus::Error::new(ruby.exception_arg_error(), err.to_string()))?
169
- .into_path();
170
- let Some(extension) = path.extension() else {
171
- continue;
172
- };
173
- if extension == "ttf" || extension == "otf" {
174
- default_fonts.push(path);
175
- }
176
- }
177
-
178
132
  let mut world = SystemWorld::builder(root, input)
179
133
  .inputs(Dict::from_iter(
180
134
  sys_inputs
@@ -183,6 +137,7 @@ fn to_png(
183
137
  ))
184
138
  .font_paths(font_paths)
185
139
  .ignore_system_fonts(ignore_system_fonts)
140
+ .ignore_embedded_fonts(ignore_embedded_fonts)
186
141
  .build()
187
142
  .map_err(|msg| magnus::Error::new(ruby.exception_arg_error(), msg.to_string()))?;
188
143
 
@@ -198,8 +153,8 @@ fn to_pdf(
198
153
  input: PathBuf,
199
154
  root: Option<PathBuf>,
200
155
  font_paths: Vec<PathBuf>,
201
- resource_path: PathBuf,
202
156
  ignore_system_fonts: bool,
157
+ ignore_embedded_fonts: bool,
203
158
  sys_inputs: HashMap<String, String>,
204
159
  pdf_standards: Vec<String>,
205
160
  ) -> Result<Vec<Vec<u8>>, Error> {
@@ -215,22 +170,6 @@ fn to_pdf(
215
170
  PathBuf::new()
216
171
  };
217
172
 
218
- let resource_path = resource_path.canonicalize()
219
- .map_err(|err| magnus::Error::new(ruby.exception_arg_error(), err.to_string()))?;
220
-
221
- let mut default_fonts = Vec::new();
222
- for entry in walkdir::WalkDir::new(resource_path.join("fonts")) {
223
- let path = entry
224
- .map_err(|err| magnus::Error::new(ruby.exception_arg_error(), err.to_string()))?
225
- .into_path();
226
- let Some(extension) = path.extension() else {
227
- continue;
228
- };
229
- if extension == "ttf" || extension == "otf" {
230
- default_fonts.push(path);
231
- }
232
- }
233
-
234
173
  let mut world = SystemWorld::builder(root, input)
235
174
  .inputs(Dict::from_iter(
236
175
  sys_inputs
@@ -239,6 +178,7 @@ fn to_pdf(
239
178
  ))
240
179
  .font_paths(font_paths)
241
180
  .ignore_system_fonts(ignore_system_fonts)
181
+ .ignore_embedded_fonts(ignore_embedded_fonts)
242
182
  .build()
243
183
  .map_err(|msg| magnus::Error::new(ruby.exception_arg_error(), msg.to_string()))?;
244
184
 
@@ -287,8 +227,8 @@ fn query(
287
227
  input: PathBuf,
288
228
  root: Option<PathBuf>,
289
229
  font_paths: Vec<PathBuf>,
290
- resource_path: PathBuf,
291
230
  ignore_system_fonts: bool,
231
+ ignore_embedded_fonts: bool,
292
232
  sys_inputs: HashMap<String, String>,
293
233
  ) -> Result<String, Error> {
294
234
  let format = match format.unwrap().to_ascii_lowercase().as_str() {
@@ -309,22 +249,6 @@ fn query(
309
249
  PathBuf::new()
310
250
  };
311
251
 
312
- let resource_path = resource_path.canonicalize()
313
- .map_err(|err| magnus::Error::new(ruby.exception_arg_error(), err.to_string()))?;
314
-
315
- let mut default_fonts = Vec::new();
316
- for entry in walkdir::WalkDir::new(resource_path.join("fonts")) {
317
- let path = entry
318
- .map_err(|err| magnus::Error::new(ruby.exception_arg_error(), err.to_string()))?
319
- .into_path();
320
- let Some(extension) = path.extension() else {
321
- continue;
322
- };
323
- if extension == "ttf" || extension == "otf" {
324
- default_fonts.push(path);
325
- }
326
- }
327
-
328
252
  let mut world = SystemWorld::builder(root, input)
329
253
  .inputs(Dict::from_iter(
330
254
  sys_inputs
@@ -333,6 +257,7 @@ fn query(
333
257
  ))
334
258
  .font_paths(font_paths)
335
259
  .ignore_system_fonts(ignore_system_fonts)
260
+ .ignore_embedded_fonts(ignore_embedded_fonts)
336
261
  .build()
337
262
  .map_err(|msg| magnus::Error::new(ruby.exception_arg_error(), msg.to_string()))?;
338
263
 
@@ -3,8 +3,10 @@ use ecow::{eco_format, EcoString};
3
3
  use serde::Serialize;
4
4
  use typst::diag::{bail, StrResult, Warned};
5
5
  use typst::engine::Sink;
6
- use typst::foundations::{Content, IntoValue, LocatableSelector, Scope};
7
- use typst::layout::PagedDocument;
6
+ use typst::foundations::{Content, Context, IntoValue, LocatableSelector, Scope};
7
+ use typst::introspection::{EmptyIntrospector, Introspector};
8
+ use typst::routines::SpanMode;
9
+ use typst_layout::PagedDocument;
8
10
  use typst::syntax::Span;
9
11
  use typst::syntax::SyntaxMode;
10
12
  use typst::World;
@@ -73,13 +75,15 @@ fn retrieve(
73
75
  document: &PagedDocument,
74
76
  ) -> StrResult<Vec<Content>> {
75
77
  let selector = eval_string(
76
- &typst::ROUTINES,
77
78
  world.track(),
79
+ world.library(),
78
80
  Sink::new().track_mut(),
81
+ EmptyIntrospector.track(),
82
+ Context::none().track(),
79
83
  &command.selector,
80
- Span::detached(),
84
+ SpanMode::Uniform(Span::detached()),
81
85
  SyntaxMode::Code,
82
- Scope::default(),
86
+ Scope::default()
83
87
  )
84
88
  .map_err(|errors| {
85
89
  let mut message = EcoString::from("failed to evaluate selector");
@@ -93,7 +97,7 @@ fn retrieve(
93
97
  .map_err(|e| e.message().clone())?;
94
98
 
95
99
  Ok(document
96
- .introspector
100
+ .introspector()
97
101
  .query(&selector.0)
98
102
  .into_iter()
99
103
  .collect::<Vec<_>>())
@@ -1,44 +1,34 @@
1
1
  use std::fs;
2
2
  use std::path::{Path, PathBuf};
3
- use std::sync::{Mutex, OnceLock};
3
+ use std::sync::{Arc, Mutex, OnceLock};
4
+ use std::collections::HashMap;
4
5
 
5
- use chrono::{DateTime, Datelike, Local};
6
- use ecow::eco_format;
6
+ use chrono::{DateTime, Datelike, FixedOffset, Local};
7
7
  use typst::diag::{FileError, FileResult, StrResult};
8
- use typst::foundations::{Bytes, Datetime, Dict};
9
- use typst::syntax::{FileId, Lines, Source, VirtualPath};
8
+ use typst::foundations::{Bytes, Datetime, Dict, Duration};
9
+ use typst::syntax::{FileId, Lines, Source, VirtualPath, VirtualRoot, RootedPath};
10
10
  use typst::text::{Font, FontBook};
11
11
  use typst::utils::LazyHash;
12
12
  use typst::{Features, Library, LibraryExt, World};
13
- use typst_kit::{
14
- fonts::{FontSearcher, FontSlot},
15
- package::PackageStorage,
16
- };
17
-
18
- use std::collections::HashMap;
19
-
20
- use crate::download::SlientDownload;
13
+ use typst_kit::fonts::{self, FontStore};
14
+ use typst_kit::packages::{FsPackages, SystemPackages, UniversePackages};
21
15
 
22
16
  /// A world that provides access to the operating system.
23
17
  pub struct SystemWorld {
24
18
  /// The working directory.
25
19
  workdir: Option<PathBuf>,
26
- /// The canonical path to the input file.
27
- input: PathBuf,
28
20
  /// The root relative to which absolute paths are resolved.
29
21
  root: PathBuf,
30
22
  /// The input path.
31
23
  main: FileId,
32
24
  /// Typst's standard library.
33
25
  library: LazyHash<Library>,
34
- /// Metadata about discovered fonts.
35
- book: LazyHash<FontBook>,
36
26
  /// Locations of and storage for lazily loaded fonts.
37
- fonts: Vec<FontSlot>,
27
+ fonts: Arc<FontStore>,
38
28
  /// Maps file ids to source files and buffers.
39
29
  slots: Mutex<HashMap<FileId, FileSlot>>,
40
30
  /// Holds information about where packages are stored.
41
- package_storage: PackageStorage,
31
+ package_storage: SystemPackages,
42
32
  /// The current datetime if requested. This is stored here to ensure it is
43
33
  /// always the same within one compilation. Reset between compilations.
44
34
  now: OnceLock<DateTime<Local>>,
@@ -50,7 +40,7 @@ impl World for SystemWorld {
50
40
  }
51
41
 
52
42
  fn book(&self) -> &LazyHash<FontBook> {
53
- &self.book
43
+ &self.fonts.book()
54
44
  }
55
45
 
56
46
  fn main(&self) -> FileId {
@@ -66,21 +56,30 @@ impl World for SystemWorld {
66
56
  }
67
57
 
68
58
  fn font(&self, index: usize) -> Option<Font> {
69
- self.fonts[index].get()
59
+ self.fonts.font(index)
70
60
  }
71
61
 
72
- fn today(&self, offset: Option<i64>) -> Option<Datetime> {
62
+ fn today(&self, offset: Option<Duration>) -> Option<Datetime> {
73
63
  let now = self.now.get_or_init(chrono::Local::now);
74
64
 
75
- let naive = match offset {
76
- None => now.naive_local(),
77
- Some(o) => now.naive_utc() + chrono::Duration::hours(o),
65
+ let now = match offset {
66
+ None => now.fixed_offset(),
67
+ Some(offset) => {
68
+ let seconds = offset.seconds().trunc();
69
+ if !seconds.is_finite()
70
+ || seconds < f64::from(i32::MIN)
71
+ || seconds > f64::from(i32::MAX)
72
+ {
73
+ return None;
74
+ }
75
+ now.with_timezone(&FixedOffset::east_opt(seconds as i32)?)
76
+ }
78
77
  };
79
78
 
80
79
  Datetime::from_ymd(
81
- naive.year(),
82
- naive.month().try_into().ok()?,
83
- naive.day().try_into().ok()?,
80
+ now.year(),
81
+ now.month().try_into().ok()?,
82
+ now.day().try_into().ok()?,
84
83
  )
85
84
  }
86
85
  }
@@ -122,23 +121,20 @@ impl SystemWorld {
122
121
  self.now.take();
123
122
  }
124
123
 
125
- /// Return the canonical path to the input file.
126
- pub fn input(&self) -> &PathBuf {
127
- &self.input
128
- }
129
-
130
124
  /// Lookup a source file by id.
131
125
  #[track_caller]
132
126
  pub fn lookup(&self, id: FileId) -> Lines<String> {
133
- // self.source(id)
134
- // .expect("file id does not point to any source file")
135
127
  self.slot(id, |slot| {
136
128
  if let Some(source) = slot.source.get() {
137
129
  let source = source.as_ref().expect("file is not valid");
138
130
  source.lines().clone()
139
131
  } else if let Some(bytes) = slot.file.get() {
140
132
  let bytes = bytes.as_ref().expect("file is not valid");
141
- Lines::try_from(bytes).expect("file is not valid utf-8")
133
+ Lines::new(
134
+ decode_utf8(bytes.as_slice())
135
+ .expect("file is not valid utf-8")
136
+ .to_string(),
137
+ )
142
138
  } else {
143
139
  panic!("file id does not point to any source file");
144
140
  }
@@ -151,8 +147,11 @@ pub struct SystemWorldBuilder {
151
147
  main: PathBuf,
152
148
  font_paths: Vec<PathBuf>,
153
149
  ignore_system_fonts: bool,
150
+ ignore_embedded_fonts: bool,
154
151
  inputs: Dict,
155
152
  features: Features,
153
+ package_path: Option<PathBuf>,
154
+ package_cache_path: Option<PathBuf>,
156
155
  }
157
156
 
158
157
  impl SystemWorldBuilder {
@@ -162,8 +161,11 @@ impl SystemWorldBuilder {
162
161
  main,
163
162
  font_paths: Vec::new(),
164
163
  ignore_system_fonts: false,
164
+ ignore_embedded_fonts: false,
165
165
  inputs: Dict::default(),
166
166
  features: Features::default(),
167
+ package_path: None,
168
+ package_cache_path: None,
167
169
  }
168
170
  }
169
171
 
@@ -177,6 +179,11 @@ impl SystemWorldBuilder {
177
179
  self
178
180
  }
179
181
 
182
+ pub fn ignore_embedded_fonts(mut self, ignore: bool) -> Self {
183
+ self.ignore_embedded_fonts = ignore;
184
+ self
185
+ }
186
+
180
187
  pub fn inputs(mut self, inputs: Dict) -> Self {
181
188
  self.inputs = inputs;
182
189
  self
@@ -188,33 +195,57 @@ impl SystemWorldBuilder {
188
195
  }
189
196
 
190
197
  pub fn build(self) -> StrResult<SystemWorld> {
191
- let fonts = FontSearcher::new()
192
- .include_system_fonts(!self.ignore_system_fonts)
193
- .search_with(&self.font_paths);
198
+ let fonts = Arc::new(build_font_store(self.ignore_system_fonts, self.ignore_embedded_fonts, self.font_paths));
199
+
200
+ let package_storage = system_packages(self.package_path, self.package_cache_path);
194
201
 
195
- let input = self.main.canonicalize().map_err(|_| {
196
- eco_format!("input file not found (searched at {})", self.main.display())
197
- })?;
198
202
  // Resolve the virtual path of the main file within the project root.
199
- let main_path = VirtualPath::within_root(&self.main, &self.root)
200
- .ok_or("input file must be contained in project root")?;
203
+ let main_path = VirtualPath::virtualize(&self.root, &self.main)
204
+ .map_err(|_| "input file must be contained in project root")?;
201
205
 
202
206
  let world = SystemWorld {
203
207
  workdir: std::env::current_dir().ok(),
204
- input,
205
208
  root: self.root,
206
- main: FileId::new(None, main_path),
209
+ main: RootedPath::new(VirtualRoot::Project, main_path).intern(),
207
210
  library: LazyHash::new(Library::builder().with_inputs(self.inputs).with_features(self.features).build()),
208
- book: LazyHash::new(fonts.book),
209
- fonts: fonts.fonts,
211
+ fonts,
210
212
  slots: Mutex::default(),
211
- package_storage: PackageStorage::new(None, None, crate::download::downloader()),
213
+ package_storage,
212
214
  now: OnceLock::new(),
213
215
  };
214
216
  Ok(world)
215
217
  }
216
218
  }
217
219
 
220
+ fn build_font_store(ignore_system_fonts: bool, ignore_embedded_fonts: bool, font_paths: Vec<PathBuf>) -> FontStore {
221
+ let mut fonts = FontStore::new();
222
+ if !ignore_system_fonts {
223
+ fonts.extend(fonts::system());
224
+ }
225
+ if !ignore_embedded_fonts {
226
+ fonts.extend(fonts::embedded());
227
+ }
228
+ for path in font_paths {
229
+ fonts.extend(fonts::scan(&path));
230
+ }
231
+ fonts
232
+ }
233
+
234
+ fn system_packages(
235
+ package_path: Option<PathBuf>,
236
+ package_cache_path: Option<PathBuf>,
237
+ ) -> SystemPackages {
238
+ SystemPackages::from_parts(
239
+ package_path
240
+ .map(FsPackages::new)
241
+ .or_else(FsPackages::system_data),
242
+ package_cache_path
243
+ .map(FsPackages::new)
244
+ .or_else(FsPackages::system_cache),
245
+ UniversePackages::new(crate::download::downloader()),
246
+ )
247
+ }
248
+
218
249
  /// Holds canonical data for all paths pointing to the same entity.
219
250
  ///
220
251
  /// Both fields can be populated if the file is both imported and read().
@@ -244,7 +275,7 @@ impl FileSlot {
244
275
  self.file.reset();
245
276
  }
246
277
 
247
- fn source(&mut self, root: &Path, package_storage: &PackageStorage) -> FileResult<Source> {
278
+ fn source(&mut self, root: &Path, package_storage: &SystemPackages) -> FileResult<Source> {
248
279
  let id = self.id;
249
280
  self.source.get_or_init(
250
281
  || system_path(root, id, package_storage),
@@ -260,7 +291,7 @@ impl FileSlot {
260
291
  )
261
292
  }
262
293
 
263
- fn file(&mut self, root: &Path, package_storage: &PackageStorage) -> FileResult<Bytes> {
294
+ fn file(&mut self, root: &Path, package_storage: &SystemPackages) -> FileResult<Bytes> {
264
295
  let id = self.id;
265
296
  self.file.get_or_init(
266
297
  || system_path(root, id, package_storage),
@@ -270,19 +301,21 @@ impl FileSlot {
270
301
  }
271
302
 
272
303
  /// The path of the slot on the system.
273
- fn system_path(root: &Path, id: FileId, package_storage: &PackageStorage) -> FileResult<PathBuf> {
304
+ fn system_path(root: &Path, id: FileId, package_storage: &SystemPackages) -> FileResult<PathBuf> {
274
305
  // Determine the root path relative to which the file path
275
306
  // will be resolved.
276
- let buf;
277
- let mut root = root;
278
- if let Some(spec) = id.package() {
279
- buf = package_storage.prepare_package(spec, &mut SlientDownload(&spec))?;
280
- root = &buf;
281
- }
307
+ let package_root;
308
+ let root = match id.root() {
309
+ VirtualRoot::Project => root,
310
+ VirtualRoot::Package(spec) => {
311
+ package_root = package_storage.obtain(spec)?;
312
+ package_root.path()
313
+ }
314
+ };
282
315
 
283
316
  // Join the path to the root. If it tries to escape, deny
284
317
  // access. Note: It can still escape via symlinks.
285
- id.vpath().resolve(root).ok_or(FileError::AccessDenied)
318
+ id.vpath().realize(root).map_err(Into::into)
286
319
  }
287
320
 
288
321
  /// Lazily processes data for a file.
data/lib/base.rb CHANGED
@@ -34,14 +34,14 @@ module Typst
34
34
  options[:dependencies] ||= {}
35
35
  options[:fonts] ||= {}
36
36
  options[:sys_inputs] ||= {}
37
- options[:resource_path] ||= File.dirname(__FILE__)
38
37
  options[:ignore_system_fonts] ||= false
38
+ options[:ignore_embedded_fonts] ||= false
39
39
 
40
40
  self.options = options
41
41
  end
42
42
 
43
43
  def typst_args
44
- [options[:file], options[:root], options[:font_paths], options[:resource_path], options[:ignore_system_fonts], options[:sys_inputs].map{ |k,v| [k.to_s,v.to_s] }.to_h]
44
+ [options[:file], options[:root], options[:font_paths], options[:ignore_system_fonts], options[:ignore_embedded_fonts], options[:sys_inputs].map{ |k,v| [k.to_s,v.to_s] }.to_h]
45
45
  end
46
46
 
47
47
  def typst_pdf_args
@@ -125,14 +125,14 @@ module Typst
125
125
  query_options = { field: field, one: one, format: format }
126
126
 
127
127
  if self.options.has_key?(:file)
128
- Typst::Query.new(selector, self.options[:file], **query_options.merge(self.options.slice(:root, :font_paths, :resource_path, :ignore_system_fonts, :sys_inputs)))
128
+ Typst::Query.new(selector, self.options[:file], **query_options.merge(self.options.slice(:root, :font_paths, :ignore_system_fonts, :ignore_embedded_fonts, :sys_inputs)))
129
129
  elsif self.options.has_key?(:body)
130
130
  Typst::build_world_from_s(self.options[:body], **self.options) do |opts|
131
- Typst::Query.new(selector, opts[:file], **query_options.merge(opts.slice(:root, :font_paths, :resource_path, :ignore_system_fonts, :sys_inputs)))
131
+ Typst::Query.new(selector, opts[:file], **query_options.merge(opts.slice(:root, :font_paths, :ignore_system_fonts, :ignore_embedded_fonts, :sys_inputs)))
132
132
  end
133
133
  elsif self.options.has_key?(:zip)
134
134
  Typst::build_world_from_zip(self.options[:zip], **self.options) do |opts|
135
- Typst::Query.new(selector, opts[:file], **query_options.merge(opts.slice(:root, :font_paths, :resource_path, :ignore_system_fonts, :sys_inputs)))
135
+ Typst::Query.new(selector, opts[:file], **query_options.merge(opts.slice(:root, :font_paths, :ignore_system_fonts, :ignore_embedded_fonts, :sys_inputs)))
136
136
  end
137
137
  else
138
138
  raise "No input given"
data/lib/query.rb CHANGED
@@ -2,9 +2,9 @@ module Typst
2
2
  class Query < Base
3
3
  attr_accessor :format
4
4
 
5
- def initialize(selector, input, field: nil, one: false, format: "json", root: ".", font_paths: [], resource_path: ".", ignore_system_fonts: false, sys_inputs: {})
5
+ def initialize(selector, input, field: nil, one: false, format: "json", root: ".", font_paths: [], ignore_system_fonts: false, ignore_embedded_fonts: false, sys_inputs: {})
6
6
  self.format = format
7
- @result = Typst::_query(selector, field, one, format, input, root, font_paths, resource_path, ignore_system_fonts, sys_inputs)
7
+ @result = Typst::_query(selector, field, one, format, input, root, font_paths, ignore_system_fonts, ignore_embedded_fonts, sys_inputs)
8
8
  end
9
9
 
10
10
  def result(raw: false)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: typst
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.14.2.3
4
+ version: 0.15.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Flinn
@@ -18,7 +18,7 @@ dependencies:
18
18
  version: '0.9'
19
19
  - - ">="
20
20
  - !ruby/object:Gem::Version
21
- version: 0.9.124
21
+ version: 0.9.128
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -28,7 +28,7 @@ dependencies:
28
28
  version: '0.9'
29
29
  - - ">="
30
30
  - !ruby/object:Gem::Version
31
- version: 0.9.124
31
+ version: 0.9.128
32
32
  - !ruby/object:Gem::Dependency
33
33
  name: rubyzip
34
34
  requirement: !ruby/object:Gem::Requirement
@@ -113,27 +113,12 @@ files:
113
113
  - ext/typst/extconf.rb
114
114
  - ext/typst/src/compiler.rs
115
115
  - ext/typst/src/download.rs
116
- - ext/typst/src/fonts.rs
117
116
  - ext/typst/src/lib.rs
118
117
  - ext/typst/src/package.rs
119
118
  - ext/typst/src/query.rs
120
119
  - ext/typst/src/world.rs
121
120
  - lib/base.rb
122
121
  - lib/document.rb
123
- - lib/fonts/DejaVuSansMono-Bold.ttf
124
- - lib/fonts/DejaVuSansMono-BoldOblique.ttf
125
- - lib/fonts/DejaVuSansMono-Oblique.ttf
126
- - lib/fonts/DejaVuSansMono.ttf
127
- - lib/fonts/LinLibertine_R.ttf
128
- - lib/fonts/LinLibertine_RB.ttf
129
- - lib/fonts/LinLibertine_RBI.ttf
130
- - lib/fonts/LinLibertine_RI.ttf
131
- - lib/fonts/NewCM10-Bold.otf
132
- - lib/fonts/NewCM10-BoldItalic.otf
133
- - lib/fonts/NewCM10-Italic.otf
134
- - lib/fonts/NewCM10-Regular.otf
135
- - lib/fonts/NewCMMath-Book.otf
136
- - lib/fonts/NewCMMath-Regular.otf
137
122
  - lib/formats/html_experimental.rb
138
123
  - lib/formats/pdf.rb
139
124
  - lib/formats/png.rb
@@ -158,7 +143,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
158
143
  - !ruby/object:Gem::Version
159
144
  version: '0'
160
145
  requirements: []
161
- rubygems_version: 4.0.4
146
+ rubygems_version: 4.0.15
162
147
  specification_version: 4
163
148
  summary: Ruby binding to typst, a new markup-based typesetting system that is powerful
164
149
  and easy to learn.
@@ -1,86 +0,0 @@
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
- }
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file