typst 0.13.3-x86_64-linux

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: cf9aca0775b06cb67dc4a73316b48a97a0860dee2cad76e774c7cfa90d692890
4
+ data.tar.gz: 03a4e6871ee9ea6f0efe59d8af10682e8e777d3d51cfd85e812ec03d990a8438
5
+ SHA512:
6
+ metadata.gz: 3d7b7ffead28fb58eac7ae7558f71d6ea9cac0ac3d9d21d2c4d6ac53a3e3f6a209520581dc78f1c3e9c1e34e3ebaa2aa16363f4f64a2a5d7464210c0ab18c4e3
7
+ data.tar.gz: 57201f8fa580ff4553677f5183f7313170d79534be10775d952d3c3294c14943d15ee59e9500cfc1972c0aba35e1a5a1dbd74dbc722fc3d0e0e3ab1859621f67
data/Cargo.toml ADDED
@@ -0,0 +1,3 @@
1
+ [workspace]
2
+ members = ["ext/typst"]
3
+ resolver = "2"
data/README.md ADDED
@@ -0,0 +1,133 @@
1
+ # typst-rb
2
+
3
+ Ruby binding to [typst](https://github.com/typst/typst),
4
+ a new markup-based typesetting system that is powerful and easy to learn.
5
+
6
+ ## Installation
7
+
8
+ ```bash
9
+ gem install typst
10
+ ```
11
+
12
+ ## Usage
13
+
14
+ ```ruby
15
+ require "typst"
16
+
17
+ # Compile a typst file and write the output to a PDF file
18
+ Typst("readme.typ").compile(:pdf).write("readme.pdf")
19
+
20
+ # Use a typst file `readme.typ`
21
+ t = Typst("readme.typ")
22
+
23
+ # Use a typst string
24
+ t = Typst(body: %{hello world})
25
+
26
+ # Use a typst file in a zip file
27
+ t = Typst(zip: "test/main.typ.zip")
28
+
29
+ # Compile to PDF
30
+ f = t.compile(:pdf)
31
+
32
+ # Compile to SVG
33
+ f = t.compile(:svg)
34
+
35
+ # Compile to PNG
36
+ f = t.compile(:png)
37
+
38
+ # Compile to SVGs enveloped in HTML
39
+ # Depracation warning: this feature will go away once Typst HTML moves out of experimental
40
+ f = t.compile(:html, title: "Typst+Ruby")
41
+
42
+ # Compile to HTML (using Typst expirmental HTML)
43
+ f = t.compile(:html_experimental)
44
+
45
+ # Access PDF or HTML output as a string
46
+ # Note: For PDF and PNG this will give data, for SVG and HTML this will give markup
47
+ Typst("readme.typ").compile(:pdf).document
48
+ # => "%PDF-1.7\n%\x80\x80\x80\x80\n\n4 0 obj\n<<\n /Type /Font\n /Subtype ..."
49
+ Typst("readme.typ").compile(:html).document
50
+ # => "\n<!DOCTYPE html>\n<html>\n<head>\n<title>main</title>\n</head>\n<body>\n<svg class=\"typst-doc\" ...
51
+
52
+ # Or return content as an array of bytes
53
+ pdf_bytes = Typst("readme.typ").compile(:pdf).bytes
54
+ # => [37, 80, 68, 70, 45, 49, 46, 55, 10, 37, 128 ...]
55
+
56
+ # Write the output to a file
57
+ # Note: for multi-page documents using formats other than PDF, pages write to multiple files, e.g. `readme_0.png`, `readme_1.png`
58
+ f.write("filename.pdf")
59
+
60
+ # Return SVG, HTML or PNG content as an array of pages
61
+ Typst("readme.typ").compile(:svg).pages
62
+ # => ["<svg class=\"typst-doc\" viewBox=\"0 0 595.2764999999999 841.89105\" ..."
63
+ Typst("readme.typ").compile(:html).pages
64
+ # => ["<svg class=\"typst-doc\" viewBox=\"0 0 595.2764999999999 841.89105\" ..."
65
+ Typst("readme.typ").compile(:png).pages
66
+ # => ["\x89PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x04\xA7\x00\x00\x06\x94\b\ ...
67
+
68
+ # Pass values into typst using sys_inputs
69
+ sys_inputs_example = %{
70
+ #let persons = json(bytes(sys.inputs.persons))
71
+
72
+ #for person in persons [
73
+ #person.name is #person.age years old.\\
74
+ ]
75
+ }
76
+ people = [{"name" => "John", "age" => 35}, {"name" => "Xoliswa", "age" => 45}]
77
+ data = { "persons" => people.to_json }
78
+ Typst(body: sys_inputs_example, sys_inputs: data).compile(:pdf).write("sys_inputs_example.pdf")
79
+
80
+ # Apply inputs to typst to product multiple PDFs
81
+
82
+ t = Typst(body: sys_inputs_example)
83
+ people.each do |person|
84
+ t.with_inputs({ "persons" => [person].to_json }).compile(:pdf).write("#{person['name']}.pdf")
85
+ end
86
+
87
+ # A more complex example of compiling from string
88
+ main = %{
89
+ #import "template.typ": *
90
+
91
+ #show: template.with()
92
+
93
+ #lorem(50)
94
+
95
+ #image("icon.svg")
96
+ }
97
+
98
+ template = %{
99
+ #let template(body) = {
100
+ set text(12pt, font: "Example")
101
+ body
102
+ }
103
+ }
104
+
105
+ icon = File.read("icon.svg")
106
+ font_bytes = File.read("Example.ttf")
107
+
108
+ Typst(body: main, dependencies: { "template.typ" => template, "icon.svg" => icon }, fonts: { "Example.ttf" => font_bytes }).compile(:pdf)
109
+
110
+ # From a zip with a named main typst file
111
+ Typst(zip: "test/main.typ.zip", main_file: "hello.typ").compile(:pdf)
112
+
113
+ Typst::Query.new("heading", "readme.typ").result
114
+ # =>
115
+ # [{"func" => "heading",
116
+ # "level" => 1,
117
+ # "depth" => 1,
118
+ # ...
119
+
120
+ Typst::Query.new("heading", "readme.typ", format: "json").result(raw: true)
121
+ # => "[\n {\n \"func\": \"heading\",\n \"level\": 1,\n \"depth\": ..."
122
+
123
+ Typst::Query.new("heading", "readme.typ", format: "yaml").result(raw: true)
124
+ # => "- func: heading\n level: 1\n depth: 1\n offset: 0\n numbering: ..."
125
+
126
+ ```
127
+
128
+ ## Contributors & Acknowledgements
129
+ typst-rb is based on [typst-py](https://github.com/messense/typst-py) by [messense](https://github.com/messense)
130
+
131
+ ## License
132
+
133
+ This work is released under the Apache-2.0 license. A copy of the license is provided in the [LICENSE](./LICENSE) file.
data/README.typ ADDED
@@ -0,0 +1,136 @@
1
+
2
+ #show link: underline
3
+ #show link: set text(blue)
4
+
5
+ = typst-rb
6
+
7
+ Ruby binding to #link("https://github.com/typst/typst")[typst], a new markup-based typesetting system that is powerful and easy to learn.
8
+
9
+ == Installation
10
+
11
+ ```bash
12
+ gem install typst
13
+ ```
14
+
15
+ == Usage
16
+
17
+ ```ruby
18
+ require "typst"
19
+
20
+ # Compile a typst file and write the output to a PDF file
21
+ Typst("readme.typ").compile(:pdf).write("readme.pdf")
22
+
23
+ # Use a typst file `readme.typ`
24
+ t = Typst("readme.typ")
25
+
26
+ # Use a typst string
27
+ t = Typst(body: %{hello world})
28
+
29
+ # Use a typst file in a zip file
30
+ t = Typst(zip: "test/main.typ.zip")
31
+
32
+ # Compile to PDF
33
+ f = t.compile(:pdf)
34
+
35
+ # Compile to SVG
36
+ f = t.compile(:svg)
37
+
38
+ # Compile to PNG
39
+ f = t.compile(:png)
40
+
41
+ # Compile to SVGs enveloped in HTML
42
+ # Depracation warning: this feature will go away once Typst HTML moves out of experimental
43
+ f = t.compile(:html, title: "Typst+Ruby")
44
+
45
+ # Compile to HTML (using Typst expirmental HTML)
46
+ f = t.compile(:html_experimental)
47
+
48
+ # Access PDF or HTML output as a string
49
+ # Note: For PDF and PNG this will give data, for SVG and HTML this will give markup
50
+ Typst("readme.typ").compile(:pdf).document
51
+ # => "%PDF-1.7\n%\x80\x80\x80\x80\n\n4 0 obj\n<<\n /Type /Font\n /Subtype ..."
52
+ Typst("readme.typ").compile(:html).document
53
+ # => "\n<!DOCTYPE html>\n<html>\n<head>\n<title>main</title>\n</head>\n<body>\n<svg class=\"typst-doc\" ...
54
+
55
+ # Or return content as an array of bytes
56
+ pdf_bytes = Typst("readme.typ").compile(:pdf).bytes
57
+ # => [37, 80, 68, 70, 45, 49, 46, 55, 10, 37, 128 ...]
58
+
59
+ # Write the output to a file
60
+ # Note: for multi-page documents using formats other than PDF, pages write to multiple files, e.g. `readme_0.png`, `readme_1.png`
61
+ f.write("filename.pdf")
62
+
63
+ # Return SVG, HTML or PNG content as an array of pages
64
+ Typst("readme.typ").compile(:svg).pages
65
+ # => ["<svg class=\"typst-doc\" viewBox=\"0 0 595.2764999999999 841.89105\" ..."
66
+ Typst("readme.typ").compile(:html).pages
67
+ # => ["<svg class=\"typst-doc\" viewBox=\"0 0 595.2764999999999 841.89105\" ..."
68
+ Typst("readme.typ").compile(:png).pages
69
+ # => ["\x89PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x04\xA7\x00\x00\x06\x94\b\ ...
70
+
71
+ # Pass values into typst using sys_inputs
72
+ sys_inputs_example = %{
73
+ #let persons = json(bytes(sys.inputs.persons))
74
+
75
+ #for person in persons [
76
+ #person.name is #person.age years old.\\
77
+ ]
78
+ }
79
+ people = [{"name" => "John", "age" => 35}, {"name" => "Xoliswa", "age" => 45}]
80
+ data = { "persons" => people.to_json }
81
+ Typst(body: sys_inputs_example, sys_inputs: data).compile(:pdf).write("sys_inputs_example.pdf")
82
+
83
+ # Apply inputs to typst to product multiple PDFs
84
+
85
+ t = Typst(body: sys_inputs_example)
86
+ people.each do |person|
87
+ t.with_inputs({ "persons" => [person].to_json }).compile(:pdf).write("#{person['name']}.pdf")
88
+ end
89
+
90
+ # A more complex example of compiling from string
91
+ main = %{
92
+ #import "template.typ": *
93
+
94
+ #show: template.with()
95
+
96
+ #lorem(50)
97
+
98
+ #image("icon.svg")
99
+ }
100
+
101
+ template = %{
102
+ #let template(body) = {
103
+ set text(12pt, font: "Example")
104
+ body
105
+ }
106
+ }
107
+
108
+ icon = File.read("icon.svg")
109
+ font_bytes = File.read("Example.ttf")
110
+
111
+ Typst(body: main, dependencies: { "template.typ" => template, "icon.svg" => icon }, fonts: { "Example.ttf" => font_bytes }).compile(:pdf)
112
+
113
+ # From a zip with a named main typst file
114
+ Typst(zip: "test/main.typ.zip", main_file: "hello.typ").compile(:pdf)
115
+
116
+ Typst::Query.new("heading", "readme.typ").result
117
+ # =>
118
+ # [{"func" => "heading",
119
+ # "level" => 1,
120
+ # "depth" => 1,
121
+ # ...
122
+
123
+ Typst::Query.new("heading", "readme.typ", format: "json").result(raw: true)
124
+ # => "[\n {\n \"func\": \"heading\",\n \"level\": 1,\n \"depth\": ..."
125
+
126
+ Typst::Query.new("heading", "readme.typ", format: "yaml").result(raw: true)
127
+ # => "- func: heading\n level: 1\n depth: 1\n offset: 0\n numbering: ..."
128
+
129
+ ```
130
+
131
+ == Contributors & Acknowledgements
132
+ typst-rb is based on #link("https://github.com/messense/typst-py")[typst-py] by #link("https://github.com/messense")[messense]
133
+
134
+ == License
135
+
136
+ This work is released under the Apache-2.0 license. A copy of the license is provided in the #link("https://github.com/actsasflinn/typst-rb/blob/main/LICENSE")[LICENSE] file.
data/Rakefile ADDED
@@ -0,0 +1,31 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/extensiontask"
3
+ require "rake/testtask"
4
+ require "rubygems/package_task"
5
+ require "bundler"
6
+
7
+ CROSS_PLATFORMS = %w[
8
+ aarch64-linux
9
+ arm64-darwin
10
+ x64-mingw32
11
+ x86_64-darwin
12
+ x86_64-linux
13
+ x86_64-linux-musl
14
+ ]
15
+
16
+ spec = Bundler.load_gemspec("typst.gemspec")
17
+
18
+ Gem::PackageTask.new(spec).define
19
+
20
+ Rake::ExtensionTask.new("typst", spec) do |ext|
21
+ ext.lib_dir = "lib/typst"
22
+ ext.source_pattern = "*.{rs,toml}"
23
+ ext.cross_compile = true
24
+ ext.cross_platform = CROSS_PLATFORMS
25
+ end
26
+
27
+ Rake::TestTask.new do |t|
28
+ t.libs << "test"
29
+ t.test_files = FileList['test/*_test.rb']
30
+ t.verbose = true
31
+ end
@@ -0,0 +1,54 @@
1
+ [package]
2
+ name = "typst"
3
+ version = "0.13.0"
4
+ edition = "2021"
5
+
6
+ [lib]
7
+ crate-type = ["cdylib"]
8
+
9
+ [dependencies]
10
+ chrono = { version = "0.4.38", default-features = false, features = [
11
+ "clock",
12
+ "std",
13
+ ] }
14
+ codespan-reporting = "0.11"
15
+ comemo = "0.4"
16
+ dirs = "5" #
17
+ ecow = "0.2"
18
+ env_logger = "0.10.1" #
19
+ env_proxy = "0.4" #
20
+ filetime = "0.2.22" #
21
+ flate2 = "1" #
22
+ fontdb = "0.15.0" #
23
+ log = "0.4.20" #
24
+ magnus = { version = "0.6" }
25
+ pathdiff = "0.2"
26
+ same-file = "1" #
27
+ siphasher = "1.0" #
28
+ tar = "0.4" #
29
+ #typst = { git = "https://github.com/typst/typst.git", tag = "v0.13.0" }
30
+ #typst-library = { git = "https://github.com/typst/typst.git", tag = "v0.13.0" }
31
+ serde = { version = "1.0.217", features = ["derive"] }
32
+ serde_json = "1"
33
+ serde_yaml = "0.9"
34
+ typst = "0.13.0"
35
+ typst-library = "0.13.0"
36
+ typst-kit = { version = "0.13.0", features = [
37
+ "downloads",
38
+ "embed-fonts",
39
+ "vendor-openssl",
40
+ ] }
41
+ typst-pdf = "0.13.0"
42
+ typst-svg = "0.13.0"
43
+ typst-html = "0.13.0"
44
+ typst-render = "0.13.0"
45
+ typst-eval = "0.13.0"
46
+ ureq = { version = "2", default-features = false, features = [
47
+ "gzip",
48
+ "socks-proxy",
49
+ ] }
50
+ walkdir = "2.4.0"
51
+
52
+ # enable rb-sys feature to test against Ruby head. This is only needed if you
53
+ # want to work with the unreleased, in-development, next version of Ruby
54
+ rb-sys = { version = "*", default-features = false, features = ["stable-api-compiled-fallback"] }
@@ -0,0 +1,4 @@
1
+ require "mkmf"
2
+ require "rb_sys/mkmf"
3
+
4
+ create_rust_makefile("typst/typst")
@@ -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
+ }