typst 0.0.5 → 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
}
|