typst 0.0.5 → 0.13.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 +4 -4
- data/README.md +38 -4
- data/README.typ +38 -4
- data/ext/typst/Cargo.toml +30 -15
- 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 +4 -8
- data/ext/typst/src/lib.wip.rs +0 -203
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 06f404ad033d71b2031a08e646614ab5340727441aa1da453aa7755ed12ea0a7
|
4
|
+
data.tar.gz: 9d7ed83cb3f3e87688b216be9ec111d5948110955ee4d3734281706d63430448
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9e4d70e2585ea792ab65fc440bcd9aaa57b1bfccdfbcddb6f37980b2fa061f9cef92668f7a7ec00c4b5b181bb67e5e9cf960e72421be6448186bc77b1bd68003
|
7
|
+
data.tar.gz: e95d548a3a9c11e4d7fb7ca6ec9acd401068c85227ce509cb10c467dbbf668a51069e683372b0525afcb77e08f71d0362b7e8ffdaa8c1b0688cc2dc831609019
|
data/README.md
CHANGED
@@ -25,29 +25,49 @@ pdf_bytes = Typst::Pdf.new("readme.typ").bytes
|
|
25
25
|
document = Typst::Pdf.new("readme.typ").document
|
26
26
|
# => "%PDF-1.7\n%\x80\x80\x80\x80\n\n4 0 obj\n<<\n /Type /Font\n /Subtype ..."
|
27
27
|
|
28
|
-
# Compile `readme.typ` to SVG and save as `
|
28
|
+
# Compile `readme.typ` to SVG and save as `readme_0.svg`, `readme_1.svg`
|
29
29
|
Typst::Svg.new("readme.typ").write("readme.svg")
|
30
30
|
|
31
31
|
# Or return SVG content as an array of pages
|
32
32
|
pages = Typst::Svg.new("readme.typ").pages
|
33
33
|
# => ["<svg class=\"typst-doc\" viewBox=\"0 0 595.2764999999999 841.89105\" ..."
|
34
34
|
|
35
|
+
# Compile `readme.typ` to PNG and save as `readme_0.png`, `readme_1.png`
|
36
|
+
Typst::Png.new("readme.typ").write("readme.png")
|
37
|
+
|
38
|
+
# Or return PNG content as an array of pages
|
39
|
+
pages = Typst::Png.new("readme.typ").pages
|
40
|
+
# => ["\x89PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x04\xA7\x00\x00\x06\x94\b\ ...
|
41
|
+
|
35
42
|
# Compile `readme.typ` to SVG and save as `readme.html`
|
36
|
-
Typst::Html.new("readme.typ", "README").write("readme.html")
|
43
|
+
Typst::Html.new("readme.typ", title: "README").write("readme.html")
|
37
44
|
|
38
45
|
# Or return HTML content
|
39
|
-
markup = Typst::Html.new("readme.typ", title: "README").
|
46
|
+
markup = Typst::Html.new("readme.typ", title: "README").document
|
40
47
|
# => "\n<!DOCTYPE html>\n<html>\n<head>\n<title>README</title>\n</head>\n<bo..."
|
41
48
|
|
49
|
+
# Use native Typst experimental HTML feature to write single frame HTML file
|
50
|
+
Typst::HtmlExperimental.new("readme.typ").write("readme.html")
|
51
|
+
|
52
|
+
# Or return single frame HTML content (using native Typst experimental HTML feature)
|
53
|
+
markup = Typst::HtmlExperimental.new("readme.typ").document
|
54
|
+
# => "<!DOCTYPE html>\n<html>\n <head>\n <meta charset=\"utf-8\">\n..."
|
55
|
+
|
42
56
|
# Compile from a string to PDF
|
43
57
|
t = Typst::Pdf.from_s(%{hello world})
|
44
58
|
|
45
59
|
# Compile from a string to SVG
|
46
60
|
t = Typst::Svg.from_s(%{hello world})
|
47
61
|
|
48
|
-
# Compile from a string to
|
62
|
+
# Compile from a string to PNG
|
63
|
+
t = Typst::Png.from_s(%{hello world})
|
64
|
+
|
65
|
+
# Compile from a string to SVG multi-frame/pages wrapped in HTML (non-native Typst)
|
49
66
|
t = Typst::Html.from_s(%{hello world})
|
50
67
|
|
68
|
+
# Compile from a string to single frame HTML (native Typst experimental feature)
|
69
|
+
t = Typst::HtmlExperimental.from_s(%{hello world})
|
70
|
+
|
51
71
|
# A more complex example of compiling from string
|
52
72
|
main = %{
|
53
73
|
#import "template.typ": *
|
@@ -77,6 +97,20 @@ Typst::Pdf::from_zip("working_directory.zip")
|
|
77
97
|
|
78
98
|
# From a zip with a named main typst file
|
79
99
|
Typst::Pdf::from_zip("working_directory.zip", "hello.typ")
|
100
|
+
|
101
|
+
Typst::Query.new("heading", "readme.typ").result
|
102
|
+
# =>
|
103
|
+
# [{"func" => "heading",
|
104
|
+
# "level" => 1,
|
105
|
+
# "depth" => 1,
|
106
|
+
# ...
|
107
|
+
|
108
|
+
Typst::Query.new("heading", "readme.typ", format: "json").result(raw: true)
|
109
|
+
# => "[\n {\n \"func\": \"heading\",\n \"level\": 1,\n \"depth\": ..."
|
110
|
+
|
111
|
+
Typst::Query.new("heading", "readme.typ", format: "yaml").result(raw: true)
|
112
|
+
# => "- func: heading\n level: 1\n depth: 1\n offset: 0\n numbering: ..."
|
113
|
+
|
80
114
|
```
|
81
115
|
|
82
116
|
## Contributors & Acknowledgements
|
data/README.typ
CHANGED
@@ -28,29 +28,49 @@ pdf_bytes = Typst::Pdf.new("readme.typ").bytes
|
|
28
28
|
document = Typst::Pdf.new("readme.typ").document
|
29
29
|
# => "%PDF-1.7\n%\x80\x80\x80\x80\n\n4 0 obj\n<<\n /Type /Font\n /Subtype ..."
|
30
30
|
|
31
|
-
# Compile `readme.typ` to SVG and save as `
|
31
|
+
# Compile `readme.typ` to SVG and save as `readme_0.svg`, `readme_1.svg`
|
32
32
|
Typst::Svg.new("readme.typ").write("readme.svg")
|
33
33
|
|
34
34
|
# Or return SVG content as an array of pages
|
35
35
|
pages = Typst::Svg.new("readme.typ").pages
|
36
36
|
# => ["<svg class=\"typst-doc\" viewBox=\"0 0 595.2764999999999 841.89105\" ..."
|
37
37
|
|
38
|
+
# Compile `readme.typ` to PNG and save as `readme_0.png`, `readme_1.png`
|
39
|
+
Typst::Png.new("readme.typ").write("readme.png")
|
40
|
+
|
41
|
+
# Or return PNG content as an array of pages
|
42
|
+
pages = Typst::Png.new("readme.typ").pages
|
43
|
+
# => ["\x89PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x04\xA7\x00\x00\x06\x94\b\ ...
|
44
|
+
|
38
45
|
# Compile `readme.typ` to SVG and save as `readme.html`
|
39
|
-
Typst::Html.new("readme.typ", "README").write("readme.html")
|
46
|
+
Typst::Html.new("readme.typ", title: "README").write("readme.html")
|
40
47
|
|
41
48
|
# Or return HTML content
|
42
|
-
markup = Typst::Html.new("readme.typ", title: "README").
|
49
|
+
markup = Typst::Html.new("readme.typ", title: "README").document
|
43
50
|
# => "\n<!DOCTYPE html>\n<html>\n<head>\n<title>README</title>\n</head>\n<bo..."
|
44
51
|
|
52
|
+
# Use native Typst experimental HTML feature to write single frame HTML file
|
53
|
+
Typst::HtmlExperimental.new("readme.typ").write("readme.html")
|
54
|
+
|
55
|
+
# Or return single frame HTML content (using native Typst experimental HTML feature)
|
56
|
+
markup = Typst::HtmlExperimental.new("readme.typ").document
|
57
|
+
# => "<!DOCTYPE html>\n<html>\n <head>\n <meta charset=\"utf-8\">\n..."
|
58
|
+
|
45
59
|
# Compile from a string to PDF
|
46
60
|
t = Typst::Pdf.from_s(%{hello world})
|
47
61
|
|
48
62
|
# Compile from a string to SVG
|
49
63
|
t = Typst::Svg.from_s(%{hello world})
|
50
64
|
|
51
|
-
# Compile from a string to
|
65
|
+
# Compile from a string to PNG
|
66
|
+
t = Typst::Png.from_s(%{hello world})
|
67
|
+
|
68
|
+
# Compile from a string to SVG multi-frame/pages wrapped in HTML (non-native Typst)
|
52
69
|
t = Typst::Html.from_s(%{hello world})
|
53
70
|
|
71
|
+
# Compile from a string to single frame HTML (native Typst experimental feature)
|
72
|
+
t = Typst::HtmlExperimental.from_s(%{hello world})
|
73
|
+
|
54
74
|
# A more complex example of compiling from string
|
55
75
|
main = %{
|
56
76
|
#import "template.typ": *
|
@@ -80,6 +100,20 @@ Typst::Pdf::from_zip("working_directory.zip")
|
|
80
100
|
|
81
101
|
# From a zip with a named main typst file
|
82
102
|
Typst::Pdf::from_zip("working_directory.zip", "hello.typ")
|
103
|
+
|
104
|
+
Typst::Query.new("heading", "readme.typ").result
|
105
|
+
# =>
|
106
|
+
# [{"func" => "heading",
|
107
|
+
# "level" => 1,
|
108
|
+
# "depth" => 1,
|
109
|
+
# ...
|
110
|
+
|
111
|
+
Typst::Query.new("heading", "readme.typ", format: "json").result(raw: true)
|
112
|
+
# => "[\n {\n \"func\": \"heading\",\n \"level\": 1,\n \"depth\": ..."
|
113
|
+
|
114
|
+
Typst::Query.new("heading", "readme.typ", format: "yaml").result(raw: true)
|
115
|
+
# => "- func: heading\n level: 1\n depth: 1\n offset: 0\n numbering: ..."
|
116
|
+
|
83
117
|
```
|
84
118
|
|
85
119
|
== Contributors & Acknowledgements
|
data/ext/typst/Cargo.toml
CHANGED
@@ -1,33 +1,48 @@
|
|
1
1
|
[package]
|
2
2
|
name = "typst"
|
3
|
-
version = "0.0
|
3
|
+
version = "0.13.0"
|
4
4
|
edition = "2021"
|
5
5
|
|
6
6
|
[lib]
|
7
7
|
crate-type = ["cdylib"]
|
8
8
|
|
9
9
|
[dependencies]
|
10
|
-
chrono = { version = "0.4.
|
10
|
+
chrono = { version = "0.4.38", default-features = false, features = [
|
11
11
|
"clock",
|
12
12
|
"std",
|
13
13
|
] }
|
14
14
|
codespan-reporting = "0.11"
|
15
|
-
comemo = "0.
|
16
|
-
dirs = "5"
|
15
|
+
comemo = "0.4"
|
16
|
+
dirs = "5" #
|
17
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"
|
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
24
|
magnus = { version = "0.6" }
|
25
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.
|
30
|
-
typst-library = { git = "https://github.com/typst/typst.git", tag = "v0.
|
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"
|
31
46
|
ureq = { version = "2", default-features = false, features = [
|
32
47
|
"gzip",
|
33
48
|
"socks-proxy",
|
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
|
}
|