typst 0.15.1.5 → 0.15.1.8
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/Cargo.toml +4 -1
- data/README.md +97 -23
- data/README.typ +96 -23
- data/Rakefile +4 -0
- data/ext/typst/src/compiler.rs +29 -9
- data/ext/typst/src/lib.rs +17 -12
- data/ext/typst/src/world.rs +76 -5
- data/lib/document.rb +11 -1
- data/lib/formats/html_experimental.rb +2 -1
- data/lib/formats/pdf.rb +2 -1
- data/lib/formats/png.rb +2 -1
- data/lib/formats/svg.rb +2 -1
- data/lib/typst.rb +14 -6
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e504e7d6117a089aea226128ffb96a606d0569184d274a31b786b40323ea2459
|
|
4
|
+
data.tar.gz: c54e606e7ec710230a13fd418b8faeb1f427342f6533ebe0cfa1402fbb0ee327
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: db255e768cc4b75a8f233494478140672721c19de71b5c7a8e9f3dc48cbe89fb4a8a372f004fd5136388ba0a6c673a9e133a003ea73dfc285bab6f371108cd50
|
|
7
|
+
data.tar.gz: ae2a297b332214c1b83ed24a0862b5288aae8ded7a7d921b198f7777ce5ef091492ff85d4484729f171416a2ab8cd9765b4234f338e2bbd7ca3bf9c6434c315d
|
data/Cargo.toml
CHANGED
data/README.md
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
# typst-rb
|
|
2
2
|
|
|
3
|
-
Ruby binding to [typst](https://github.com/typst/typst),
|
|
3
|
+
Ruby language binding to [typst](https://github.com/typst/typst),
|
|
4
4
|
a new markup-based typesetting system that is powerful and easy to learn.
|
|
5
5
|
|
|
6
|
+
## Rubygems
|
|
7
|
+
|
|
8
|
+
Source and native gems are provided for the following platforms: `aarch64-linux` `aarch64-linux-musl` `arm64-darwin` `x64-mingw-ucrt` `x86_64-darwin` `x86_64-linux` `x86_64-linux-musl`. The gems are built from CI [gem-push](https://github.com/actsasflinn/typst-rb/actions/workflows/gem-push.yml) action and can be verified against SHA256SUMS published with the [release](https://github.com/actsasflinn/typst-rb/releases).
|
|
9
|
+
|
|
6
10
|
## Installation
|
|
7
11
|
|
|
12
|
+
Add the following to your gemfile and `bundle install`
|
|
13
|
+
```ruby
|
|
14
|
+
gem 'typst', '>= 0.15.1.5'
|
|
15
|
+
```
|
|
16
|
+
or install from the command line:
|
|
8
17
|
```bash
|
|
9
18
|
gem install typst
|
|
10
19
|
```
|
|
@@ -13,69 +22,111 @@ gem install typst
|
|
|
13
22
|
|
|
14
23
|
```ruby
|
|
15
24
|
require "typst"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Hello World
|
|
28
|
+
This example compiles a typst string to PDF and writes to a file.
|
|
29
|
+
```ruby
|
|
30
|
+
Typst(body: "= Hello World\nThis is your first typst PDF").compile(:pdf).write("hello_world.pdf")
|
|
31
|
+
```
|
|
16
32
|
|
|
17
|
-
|
|
18
|
-
Typst
|
|
33
|
+
### The basics
|
|
34
|
+
This example initializes a `Typst::Base` object `t` using a string. The `t` object is basically an environment for your typst input and can take a variety of options suitable for your task which you can see in subsequent examples.
|
|
35
|
+
```ruby
|
|
36
|
+
t = Typst(body: "= Hello World\nThis is your first typst PDF")
|
|
37
|
+
```
|
|
38
|
+
This step compiles the typst input held in the `t` object and returns a `Typst::Document` object `doc`. In this case we're compiling to PDF so `doc` is a `Typst::PdfDocument` object which holds the PDF bytes and is ready to write to a file, output to a buffer, etc.
|
|
39
|
+
```ruby
|
|
40
|
+
doc = t.compile(:pdf)
|
|
19
41
|
```
|
|
42
|
+
This step writes the output to a local file in your current working directory.
|
|
43
|
+
```ruby
|
|
44
|
+
doc.write("hello_world.pdf")
|
|
45
|
+
```
|
|
46
|
+
### Different ways to setup the typst input
|
|
20
47
|
|
|
21
|
-
|
|
48
|
+
#### Use a local typst file named `example.typ`
|
|
22
49
|
```ruby
|
|
23
|
-
t = Typst("
|
|
50
|
+
t = Typst("example.typ")
|
|
24
51
|
```
|
|
25
52
|
|
|
26
|
-
|
|
53
|
+
#### Use a typst string
|
|
27
54
|
```ruby
|
|
28
55
|
t = Typst(body: %{hello world})
|
|
29
56
|
```
|
|
30
57
|
|
|
31
|
-
|
|
58
|
+
#### Use a zipped typst file
|
|
32
59
|
```ruby
|
|
33
60
|
t = Typst(zip: "test/main.typ.zip")
|
|
34
61
|
```
|
|
35
62
|
|
|
36
|
-
|
|
63
|
+
#### Use a remote typst file
|
|
64
|
+
```ruby
|
|
65
|
+
require "open-uri"
|
|
66
|
+
URI.open("https://github.com/actsasflinn/typst-rb/raw/refs/heads/main/README.typ") do |u|
|
|
67
|
+
Typst(body: u.read).compile(:pdf).write("remote_readme.pdf")
|
|
68
|
+
end
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
#### Use a remote zipped typst file
|
|
72
|
+
```ruby
|
|
73
|
+
require "open-uri"
|
|
74
|
+
URI.open("https://github.com/actsasflinn/typst-rb/raw/refs/heads/main/test/hello.typ.zip") do |u|
|
|
75
|
+
Tempfile.create do |f|
|
|
76
|
+
f.write(u.read)
|
|
77
|
+
f.rewind
|
|
78
|
+
Typst(zip: f).compile(:pdf).write("remote_zipped.pdf")
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Compiling
|
|
84
|
+
|
|
85
|
+
#### Compile to PDF
|
|
37
86
|
```ruby
|
|
38
87
|
doc = t.compile(:pdf)
|
|
39
88
|
```
|
|
40
89
|
|
|
41
|
-
|
|
90
|
+
#### Compile to PDF selecting the typst supported PdfStandard
|
|
42
91
|
```ruby
|
|
43
92
|
doc = t.compile(:pdf, pdf_standards: ["2.0"])
|
|
44
93
|
```
|
|
45
94
|
|
|
46
|
-
|
|
95
|
+
#### Compile to SVG
|
|
47
96
|
```ruby
|
|
48
97
|
doc = t.compile(:svg)
|
|
49
98
|
```
|
|
50
99
|
|
|
51
|
-
|
|
100
|
+
#### Compile to PNG
|
|
52
101
|
```ruby
|
|
53
102
|
doc = t.compile(:png)
|
|
54
103
|
```
|
|
55
104
|
|
|
56
|
-
|
|
105
|
+
#### Compile to PNG and set PPI
|
|
57
106
|
```ruby
|
|
58
107
|
doc = t.compile(:png, ppi: 72)
|
|
59
108
|
```
|
|
60
109
|
|
|
61
|
-
|
|
110
|
+
#### Compile to HTML (using Typst expirmental HTML)
|
|
62
111
|
```ruby
|
|
63
112
|
doc = t.compile(:html_experimental)
|
|
64
113
|
```
|
|
65
114
|
|
|
66
|
-
###
|
|
115
|
+
### Output
|
|
116
|
+
|
|
117
|
+
#### Return compiled content as an array of bytes
|
|
67
118
|
```ruby
|
|
68
119
|
pdf_bytes = Typst("readme.typ").compile(:pdf).bytes
|
|
69
120
|
# => [37, 80, 68, 70, 45, 49, 46, 55, 10, 37, 128 ...]
|
|
70
121
|
```
|
|
71
122
|
|
|
72
|
-
|
|
73
|
-
Note: for multi-page documents using formats other than PDF and HTML, pages write to multiple files, e.g. `
|
|
123
|
+
#### Write compiled output to a file
|
|
124
|
+
Note: for multi-page documents using formats other than PDF and HTML, pages write to multiple files, e.g. `filename_0.png`, `filename_1.png`
|
|
74
125
|
```ruby
|
|
75
126
|
doc.write("filename.pdf")
|
|
76
127
|
```
|
|
77
128
|
|
|
78
|
-
|
|
129
|
+
#### Return PDF, SVG, PNG or HTML content as an array of pages
|
|
79
130
|
```ruby
|
|
80
131
|
Typst("readme.typ").compile(:pdf).pages
|
|
81
132
|
# => ["%PDF-1.7\n%\x80\x80\x80\x80\n\n1 0 obj\n<<\n /Type /Pages\n /Count 3\n /Kids [160 0 R 162 ...
|
|
@@ -90,7 +141,9 @@ Typst("readme.typ").compile(:html_experimental).pages
|
|
|
90
141
|
# => ["<!DOCTYPE html>\n<html>\n <head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" ...
|
|
91
142
|
```
|
|
92
143
|
|
|
93
|
-
###
|
|
144
|
+
### More advanced setups
|
|
145
|
+
|
|
146
|
+
#### Pass values into typst using sys_inputs
|
|
94
147
|
```ruby
|
|
95
148
|
sys_inputs_example = %{
|
|
96
149
|
#let persons = json(bytes(sys.inputs.persons))
|
|
@@ -104,7 +157,7 @@ data = { "persons" => people.to_json }
|
|
|
104
157
|
Typst(body: sys_inputs_example, sys_inputs: data).compile(:pdf).write("sys_inputs_example.pdf")
|
|
105
158
|
```
|
|
106
159
|
|
|
107
|
-
|
|
160
|
+
#### Apply inputs to typst to product multiple PDFs
|
|
108
161
|
```ruby
|
|
109
162
|
t = Typst(body: sys_inputs_example)
|
|
110
163
|
people.each do |person|
|
|
@@ -112,7 +165,7 @@ people.each do |person|
|
|
|
112
165
|
end
|
|
113
166
|
```
|
|
114
167
|
|
|
115
|
-
|
|
168
|
+
#### A more complex example of compiling from string using other dependency typst template, svg and font resources all in memory
|
|
116
169
|
```ruby
|
|
117
170
|
main = %{
|
|
118
171
|
#import "template.typ": *
|
|
@@ -137,12 +190,12 @@ font_bytes = File.read("Example.ttf")
|
|
|
137
190
|
Typst(body: main, dependencies: { "template.typ" => template, "icon.svg" => icon }, fonts: { "Example.ttf" => font_bytes }).compile(:pdf)
|
|
138
191
|
```
|
|
139
192
|
|
|
140
|
-
|
|
193
|
+
#### Use a zip file with an alternatively named main typst file
|
|
141
194
|
```ruby
|
|
142
195
|
Typst(zip: "test/main.typ.zip", main_file: "hello.typ").compile(:pdf)
|
|
143
196
|
```
|
|
144
197
|
|
|
145
|
-
|
|
198
|
+
#### Use a package from the [Typst Universe](https://typst.app/universe)
|
|
146
199
|
Your package_example.typ file...
|
|
147
200
|
```typst
|
|
148
201
|
#import "@preview/wordometer:0.1.5": word-count, total-words
|
|
@@ -160,6 +213,25 @@ In this document, there are #total-words words all up.
|
|
|
160
213
|
Typst("package_example.typ").compile(:pdf).write("package_example.pdf")
|
|
161
214
|
```
|
|
162
215
|
|
|
216
|
+
### Compiler warnings
|
|
217
|
+
|
|
218
|
+
A compile can succeed *and* warn. The most common case is an unknown font
|
|
219
|
+
family: typst substitutes a different face, the document is produced, and
|
|
220
|
+
nothing tells you it is in the wrong typeface. Every compiled document carries
|
|
221
|
+
the warnings that came with it.
|
|
222
|
+
|
|
223
|
+
```ruby
|
|
224
|
+
doc = Typst(body: %{#set text(font: "Not Installed")\n= Hello}).compile(:pdf)
|
|
225
|
+
doc.warnings?
|
|
226
|
+
# => true
|
|
227
|
+
doc.warnings
|
|
228
|
+
# => ["warning: unknown font family: \"Not Installed\"\n ┌─ /tmp/.../main.typ:1:0\n ..."]
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Each entry is one formatted diagnostic, so they can be counted, filtered or
|
|
232
|
+
logged individually. A clean compile returns an empty array. Warnings that
|
|
233
|
+
accompany a compile *error* are still included in the raised message, as before.
|
|
234
|
+
|
|
163
235
|
### Query a typst document
|
|
164
236
|
```ruby
|
|
165
237
|
Typst("readme.typ").query("heading").result
|
|
@@ -196,7 +268,9 @@ typst-rb is based on [typst-py](https://github.com/messense/typst-py) by [messen
|
|
|
196
268
|
clear_cache was contributed by [NRicciVestmark](https://github.com/NRicciVestmark)\
|
|
197
269
|
CI improvements were contributed by [am1006](https://github.com/am1006)\
|
|
198
270
|
Defect resolutions by [adam12](https://github.com/adam12) and [walterdavis](https://github.com/walterdavis)\
|
|
199
|
-
Design suggestions by [alec-c4](https://github.com/alec-c4)
|
|
271
|
+
Design suggestions by [alec-c4](https://github.com/alec-c4)\
|
|
272
|
+
Compiler warnings were contributed by [TheSoloHacker47](https://github.com/TheSoloHacker47) \
|
|
273
|
+
Font optimization patches were contributed by [dmke](https://github.com/dmke)
|
|
200
274
|
|
|
201
275
|
## License
|
|
202
276
|
|
data/README.typ
CHANGED
|
@@ -9,10 +9,19 @@
|
|
|
9
9
|
|
|
10
10
|
= typst-rb
|
|
11
11
|
|
|
12
|
-
Ruby binding to #link("https://github.com/typst/typst")[typst], a new markup-based typesetting system that is powerful and easy to learn.
|
|
12
|
+
Ruby language binding to #link("https://github.com/typst/typst")[typst], a new markup-based typesetting system that is powerful and easy to learn.
|
|
13
|
+
|
|
14
|
+
== Rubygems
|
|
15
|
+
|
|
16
|
+
Source and native gems are provided for the following platforms: `aarch64-linux` `aarch64-linux-musl` `arm64-darwin` `x64-mingw-ucrt` `x86_64-darwin` `x86_64-linux` `x86_64-linux-musl`. The gems are built from CI #link("https://github.com/actsasflinn/typst-rb/actions/workflows/gem-push.yml")[gem-push] action and can be verified against SHA256SUMS published with the #link("https://github.com/actsasflinn/typst-rb/releases")[release].
|
|
13
17
|
|
|
14
18
|
== Installation
|
|
15
19
|
|
|
20
|
+
Add the following to your gemfile and `bundle install`
|
|
21
|
+
```ruby
|
|
22
|
+
gem 'typst', '>= 0.15.1.5'
|
|
23
|
+
```
|
|
24
|
+
or install from the command line:
|
|
16
25
|
```bash
|
|
17
26
|
gem install typst
|
|
18
27
|
```
|
|
@@ -21,69 +30,110 @@ gem install typst
|
|
|
21
30
|
|
|
22
31
|
```ruby
|
|
23
32
|
require "typst"
|
|
33
|
+
```
|
|
24
34
|
|
|
25
|
-
|
|
26
|
-
|
|
35
|
+
=== Hello World
|
|
36
|
+
This example compiles a typst string to PDF and writes to a file.
|
|
37
|
+
```ruby
|
|
38
|
+
Typst(body: "= Hello World\nThis is your first typst PDF").compile(:pdf).write("hello_world.pdf")
|
|
39
|
+
```
|
|
40
|
+
=== The basics
|
|
41
|
+
This example initializes a `Typst::Base` object `t` using a string. The `t` object is basically an environment for your typst input and can take a variety of options suitable for your task which you can see in subsequent examples.
|
|
42
|
+
```ruby
|
|
43
|
+
t = Typst(body: "= Hello World\nThis is your first typst PDF")
|
|
27
44
|
```
|
|
45
|
+
This step compiles the typst input held in the `t` object and returns a `Typst::Document` object `doc`. In this case we're compiling to PDF so `doc` is a `Typst::PdfDocument` object which holds the PDF bytes and is ready to write to a file, output to a buffer, etc.
|
|
46
|
+
```ruby
|
|
47
|
+
doc = t.compile(:pdf)
|
|
48
|
+
```
|
|
49
|
+
This step writes the output to a local file in your current working directory.
|
|
50
|
+
```ruby
|
|
51
|
+
doc.write("hello_world.pdf")
|
|
52
|
+
```
|
|
53
|
+
=== Different ways to setup the typst input
|
|
28
54
|
|
|
29
|
-
|
|
55
|
+
==== Use a local typst file named `example.typ`
|
|
30
56
|
```ruby
|
|
31
|
-
t = Typst("
|
|
57
|
+
t = Typst("example.typ")
|
|
32
58
|
```
|
|
33
59
|
|
|
34
|
-
|
|
60
|
+
==== Use a typst string
|
|
35
61
|
```ruby
|
|
36
62
|
t = Typst(body: %{hello world})
|
|
37
63
|
```
|
|
38
64
|
|
|
39
|
-
|
|
65
|
+
==== Use a zipped typst file
|
|
40
66
|
```ruby
|
|
41
67
|
t = Typst(zip: "test/main.typ.zip")
|
|
42
68
|
```
|
|
43
69
|
|
|
44
|
-
|
|
70
|
+
==== Use a remote typst file
|
|
71
|
+
```ruby
|
|
72
|
+
require "open-uri"
|
|
73
|
+
URI.open("https://github.com/actsasflinn/typst-rb/raw/refs/heads/main/README.typ") do |u|
|
|
74
|
+
Typst(body: u.read).compile(:pdf).write("remote_readme.pdf")
|
|
75
|
+
end
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
==== Use a remote zipped typst file
|
|
79
|
+
```ruby
|
|
80
|
+
require "open-uri"
|
|
81
|
+
URI.open("https://github.com/actsasflinn/typst-rb/raw/refs/heads/main/test/hello.typ.zip") do |u|
|
|
82
|
+
Tempfile.create do |f|
|
|
83
|
+
f.write(u.read)
|
|
84
|
+
f.rewind
|
|
85
|
+
Typst(zip: f).compile(:pdf).write("remote_zipped.pdf")
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
=== Compiling
|
|
91
|
+
|
|
92
|
+
==== Compile to PDF
|
|
45
93
|
```ruby
|
|
46
94
|
doc = t.compile(:pdf)
|
|
47
95
|
```
|
|
48
96
|
|
|
49
|
-
|
|
97
|
+
==== Compile to PDF selecting the typst supported PdfStandard
|
|
50
98
|
```ruby
|
|
51
99
|
doc = t.compile(:pdf, pdf_standards: ["2.0"])
|
|
52
100
|
```
|
|
53
101
|
|
|
54
|
-
|
|
102
|
+
==== Compile to SVG
|
|
55
103
|
```ruby
|
|
56
104
|
doc = t.compile(:svg)
|
|
57
105
|
```
|
|
58
106
|
|
|
59
|
-
|
|
107
|
+
==== Compile to PNG
|
|
60
108
|
```ruby
|
|
61
109
|
doc = t.compile(:png)
|
|
62
110
|
```
|
|
63
111
|
|
|
64
|
-
|
|
112
|
+
==== Compile to PNG and set PPI
|
|
65
113
|
```ruby
|
|
66
114
|
doc = t.compile(:png, ppi: 72)
|
|
67
115
|
```
|
|
68
116
|
|
|
69
|
-
|
|
117
|
+
==== Compile to HTML (using Typst expirmental HTML)
|
|
70
118
|
```ruby
|
|
71
119
|
doc = t.compile(:html_experimental)
|
|
72
120
|
```
|
|
73
121
|
|
|
74
|
-
===
|
|
122
|
+
=== Output
|
|
123
|
+
|
|
124
|
+
==== Return compiled content as an array of bytes
|
|
75
125
|
```ruby
|
|
76
126
|
pdf_bytes = Typst("readme.typ").compile(:pdf).bytes
|
|
77
127
|
# => [37, 80, 68, 70, 45, 49, 46, 55, 10, 37, 128 ...]
|
|
78
128
|
```
|
|
79
129
|
|
|
80
|
-
|
|
81
|
-
Note: for multi-page documents using formats other than PDF and HTML, pages write to multiple files, e.g. `
|
|
130
|
+
==== Write compiled output to a file
|
|
131
|
+
Note: for multi-page documents using formats other than PDF and HTML, pages write to multiple files, e.g. `filename_0.png`, `filename_1.png`
|
|
82
132
|
```ruby
|
|
83
133
|
doc.write("filename.pdf")
|
|
84
134
|
```
|
|
85
135
|
|
|
86
|
-
|
|
136
|
+
==== Return PDF, SVG, PNG or HTML content as an array of pages
|
|
87
137
|
```ruby
|
|
88
138
|
Typst("readme.typ").compile(:pdf).pages
|
|
89
139
|
# => ["%PDF-1.7\n%\x80\x80\x80\x80\n\n1 0 obj\n<<\n /Type /Pages\n /Count 3\n /Kids [160 0 R 162 ...
|
|
@@ -98,7 +148,9 @@ Typst("readme.typ").compile(:html_experimental).pages
|
|
|
98
148
|
# => ["<!DOCTYPE html>\n<html>\n <head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" ...
|
|
99
149
|
```
|
|
100
150
|
|
|
101
|
-
===
|
|
151
|
+
=== More advanced setups
|
|
152
|
+
|
|
153
|
+
==== Pass values into typst using sys_inputs
|
|
102
154
|
```ruby
|
|
103
155
|
sys_inputs_example = %{
|
|
104
156
|
#let persons = json(bytes(sys.inputs.persons))
|
|
@@ -112,7 +164,7 @@ data = { "persons" => people.to_json }
|
|
|
112
164
|
Typst(body: sys_inputs_example, sys_inputs: data).compile(:pdf).write("sys_inputs_example.pdf")
|
|
113
165
|
```
|
|
114
166
|
|
|
115
|
-
|
|
167
|
+
==== Apply inputs to typst to product multiple PDFs
|
|
116
168
|
```ruby
|
|
117
169
|
t = Typst(body: sys_inputs_example)
|
|
118
170
|
people.each do |person|
|
|
@@ -120,7 +172,7 @@ people.each do |person|
|
|
|
120
172
|
end
|
|
121
173
|
```
|
|
122
174
|
|
|
123
|
-
|
|
175
|
+
==== A more complex example of compiling from string using other dependency typst template, svg and font resources all in memory
|
|
124
176
|
```ruby
|
|
125
177
|
main = %{
|
|
126
178
|
#import "template.typ": *
|
|
@@ -145,12 +197,12 @@ font_bytes = File.read("Example.ttf")
|
|
|
145
197
|
Typst(body: main, dependencies: { "template.typ" => template, "icon.svg" => icon }, fonts: { "Example.ttf" => font_bytes }).compile(:pdf)
|
|
146
198
|
```
|
|
147
199
|
|
|
148
|
-
|
|
200
|
+
==== Use a zip file with an alternatively named main typst file
|
|
149
201
|
```ruby
|
|
150
202
|
Typst(zip: "test/main.typ.zip", main_file: "hello.typ").compile(:pdf)
|
|
151
203
|
```
|
|
152
204
|
|
|
153
|
-
|
|
205
|
+
==== Use a package from the #link("https://typst.app/universe")[Typst Universe]
|
|
154
206
|
Your package_example.typ file...
|
|
155
207
|
```typst
|
|
156
208
|
#import "@preview/wordometer:0.1.5": word-count, total-words
|
|
@@ -168,6 +220,25 @@ In this document, there are #total-words words all up.
|
|
|
168
220
|
Typst("package_example.typ").compile(:pdf).write("package_example.pdf")
|
|
169
221
|
```
|
|
170
222
|
|
|
223
|
+
=== Compiler warnings
|
|
224
|
+
|
|
225
|
+
A compile can succeed *and* warn. The most common case is an unknown font
|
|
226
|
+
family: typst substitutes a different face, the document is produced, and
|
|
227
|
+
nothing tells you it is in the wrong typeface. Every compiled document carries
|
|
228
|
+
the warnings that came with it.
|
|
229
|
+
|
|
230
|
+
```ruby
|
|
231
|
+
doc = Typst(body: %{#set text(font: "Not Installed")\n= Hello}).compile(:pdf)
|
|
232
|
+
doc.warnings?
|
|
233
|
+
# => true
|
|
234
|
+
doc.warnings
|
|
235
|
+
# => ["warning: unknown font family: \"Not Installed\"\n ┌─ /tmp/.../main.typ:1:0\n ..."]
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Each entry is one formatted diagnostic, so they can be counted, filtered or
|
|
239
|
+
logged individually. A clean compile returns an empty array. Warnings that
|
|
240
|
+
accompany a compile *error* are still included in the raised message, as before.
|
|
241
|
+
|
|
171
242
|
=== Query a typst document
|
|
172
243
|
```ruby
|
|
173
244
|
Typst("readme.typ").query("heading").result
|
|
@@ -204,7 +275,9 @@ typst-rb is based on #link("https://github.com/messense/typst-py")[typst-py] by
|
|
|
204
275
|
clear_cache was contributed by #link("https://github.com/NRicciVestmark")[NRicciVestmark]\
|
|
205
276
|
CI improvements were contributed by #link("https://github.com/am1006")[am1006]\
|
|
206
277
|
Defect resolutions by #link("https://github.com/adam12")[adam12] and #link("https://github.com/walterdavis")[walterdavis]\
|
|
207
|
-
Design suggestions by #link("https://github.com/alec-c4")[alec-c4]
|
|
278
|
+
Design suggestions by #link("https://github.com/alec-c4")[alec-c4]\
|
|
279
|
+
Compiler warnings were contributed by #link("https://github.com/TheSoloHacker47")[TheSoloHacker47] \
|
|
280
|
+
Font optimization patches were contributed by #link("https://github.com/dmke")[dmke]
|
|
208
281
|
|
|
209
282
|
== License
|
|
210
283
|
|
data/Rakefile
CHANGED
|
@@ -34,6 +34,10 @@ Rake::TestTask.new do |t|
|
|
|
34
34
|
t.verbose = true
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
+
task 'benchmark' do |t|
|
|
38
|
+
sh "bundle exec ruby benchmarks/compile_pdf.rb"
|
|
39
|
+
end
|
|
40
|
+
|
|
37
41
|
task 'gem:native' do |t|
|
|
38
42
|
CROSS_PLATFORMS.each do |platform|
|
|
39
43
|
sh "bundle exec rb-sys-dock --platform #{platform} --build"
|
data/ext/typst/src/compiler.rs
CHANGED
|
@@ -22,7 +22,7 @@ impl SystemWorld {
|
|
|
22
22
|
pdf_standards: &[typst_pdf::PdfStandard],
|
|
23
23
|
pretty: bool,
|
|
24
24
|
render_bleed: bool,
|
|
25
|
-
) -> StrResult<Vec<Vec<u8
|
|
25
|
+
) -> StrResult<(Vec<Vec<u8>>, Vec<String>)> {
|
|
26
26
|
// Reset everything and ensure that the main file is present.
|
|
27
27
|
self.reset();
|
|
28
28
|
self.source(self.main()).map_err(|err| err.to_string())?;
|
|
@@ -32,7 +32,7 @@ impl SystemWorld {
|
|
|
32
32
|
let Warned { output, warnings } = typst::compile::<HtmlDocument>(self);
|
|
33
33
|
match output {
|
|
34
34
|
Ok(document) => {
|
|
35
|
-
Ok(vec![export_html(&document, self, pretty)?])
|
|
35
|
+
Ok((vec![export_html(&document, self, pretty)?], self.format_warnings(&warnings)))
|
|
36
36
|
}
|
|
37
37
|
Err(errors) => Err(format_diagnostics(self, &errors, &warnings).unwrap().into()),
|
|
38
38
|
}
|
|
@@ -43,25 +43,45 @@ impl SystemWorld {
|
|
|
43
43
|
// Export the PDF / PNG.
|
|
44
44
|
Ok(document) => {
|
|
45
45
|
// Assert format is "pdf" or "png" or "svg"
|
|
46
|
-
match format.unwrap_or("pdf").to_ascii_lowercase().as_str() {
|
|
47
|
-
"pdf" =>
|
|
46
|
+
let buffers = match format.unwrap_or("pdf").to_ascii_lowercase().as_str() {
|
|
47
|
+
"pdf" => vec![export_pdf(
|
|
48
48
|
&document,
|
|
49
49
|
self,
|
|
50
50
|
typst_pdf::PdfStandards::new(pdf_standards)
|
|
51
51
|
.map_err(|e| eco_format!("PDF standards error: {:?}", e))
|
|
52
52
|
.at(Span::detached()).unwrap(),
|
|
53
53
|
pretty,
|
|
54
|
-
)?]
|
|
55
|
-
"png" =>
|
|
56
|
-
"svg" =>
|
|
57
|
-
fmt => Err(eco_format!("unknown format: {fmt}")),
|
|
58
|
-
}
|
|
54
|
+
)?],
|
|
55
|
+
"png" => export_image(&document, ImageExportFormat::Png, ppi, pretty, render_bleed)?,
|
|
56
|
+
"svg" => export_image(&document, ImageExportFormat::Svg, ppi, pretty, render_bleed)?,
|
|
57
|
+
fmt => return Err(eco_format!("unknown format: {fmt}")),
|
|
58
|
+
};
|
|
59
|
+
Ok((buffers, self.format_warnings(&warnings)))
|
|
59
60
|
}
|
|
60
61
|
Err(errors) => Err(format_diagnostics(self, &errors, &warnings).unwrap().into()),
|
|
61
62
|
}
|
|
62
63
|
}
|
|
63
64
|
}
|
|
64
65
|
}
|
|
66
|
+
|
|
67
|
+
/// One formatted string per warning.
|
|
68
|
+
///
|
|
69
|
+
/// A successful compile can still produce warnings — an unknown font
|
|
70
|
+
/// family being the one that bites people, because Typst substitutes a
|
|
71
|
+
/// different face and the document still generates. Previously those were
|
|
72
|
+
/// dropped on the floor: `format_diagnostics` only ever saw them on the
|
|
73
|
+
/// error path. Returning them one per entry (rather than as a single
|
|
74
|
+
/// blob) means a caller can count them, filter them, or attach them to a
|
|
75
|
+
/// log line without parsing.
|
|
76
|
+
fn format_warnings(&self, warnings: &[SourceDiagnostic]) -> Vec<String> {
|
|
77
|
+
warnings
|
|
78
|
+
.iter()
|
|
79
|
+
.map(|warning| {
|
|
80
|
+
format_diagnostics(self, &[], std::slice::from_ref(warning))
|
|
81
|
+
.unwrap_or_else(|_| warning.message.to_string())
|
|
82
|
+
})
|
|
83
|
+
.collect()
|
|
84
|
+
}
|
|
65
85
|
}
|
|
66
86
|
|
|
67
87
|
/// Export to a PDF.
|
data/ext/typst/src/lib.rs
CHANGED
|
@@ -24,7 +24,7 @@ fn to_html(
|
|
|
24
24
|
render_bleed: bool,
|
|
25
25
|
pretty: bool,
|
|
26
26
|
sys_inputs: HashMap<String, String>,
|
|
27
|
-
) -> Result<Vec<Vec<u8>>, Error> {
|
|
27
|
+
) -> Result<(Vec<Vec<u8>>, Vec<String>), Error> {
|
|
28
28
|
let input = input.canonicalize()
|
|
29
29
|
.map_err(|err| magnus::Error::new(ruby.exception_arg_error(), err.to_string()))?;
|
|
30
30
|
|
|
@@ -62,11 +62,11 @@ fn to_html(
|
|
|
62
62
|
.build()
|
|
63
63
|
.map_err(|msg| magnus::Error::new(ruby.exception_arg_error(), msg.to_string()))?;
|
|
64
64
|
|
|
65
|
-
let
|
|
65
|
+
let compiled = world
|
|
66
66
|
.compile(Some("html"), None, &Vec::new(), pretty, render_bleed)
|
|
67
67
|
.map_err(|msg| magnus::Error::new(ruby.exception_arg_error(), msg.to_string()))?;
|
|
68
68
|
|
|
69
|
-
Ok(
|
|
69
|
+
Ok(compiled)
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
fn to_svg(
|
|
@@ -79,7 +79,7 @@ fn to_svg(
|
|
|
79
79
|
render_bleed: bool,
|
|
80
80
|
pretty: bool,
|
|
81
81
|
sys_inputs: HashMap<String, String>,
|
|
82
|
-
) -> Result<Vec<Vec<u8>>, Error> {
|
|
82
|
+
) -> Result<(Vec<Vec<u8>>, Vec<String>), Error> {
|
|
83
83
|
let input = input.canonicalize()
|
|
84
84
|
.map_err(|err| magnus::Error::new(ruby.exception_arg_error(), err.to_string()))?;
|
|
85
85
|
|
|
@@ -104,11 +104,11 @@ fn to_svg(
|
|
|
104
104
|
.build()
|
|
105
105
|
.map_err(|msg| magnus::Error::new(ruby.exception_arg_error(), msg.to_string()))?;
|
|
106
106
|
|
|
107
|
-
let
|
|
107
|
+
let compiled = world
|
|
108
108
|
.compile(Some("svg"), None, &Vec::new(), pretty, render_bleed)
|
|
109
109
|
.map_err(|msg| magnus::Error::new(ruby.exception_arg_error(), msg.to_string()))?;
|
|
110
110
|
|
|
111
|
-
Ok(
|
|
111
|
+
Ok(compiled)
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
fn to_png(
|
|
@@ -121,7 +121,7 @@ fn to_png(
|
|
|
121
121
|
render_bleed: bool,
|
|
122
122
|
sys_inputs: HashMap<String, String>,
|
|
123
123
|
ppi: Option<f32>,
|
|
124
|
-
) -> Result<Vec<Vec<u8>>, Error> {
|
|
124
|
+
) -> Result<(Vec<Vec<u8>>, Vec<String>), Error> {
|
|
125
125
|
let input = input.canonicalize()
|
|
126
126
|
.map_err(|err| magnus::Error::new(ruby.exception_arg_error(), err.to_string()))?;
|
|
127
127
|
|
|
@@ -146,11 +146,11 @@ fn to_png(
|
|
|
146
146
|
.build()
|
|
147
147
|
.map_err(|msg| magnus::Error::new(ruby.exception_arg_error(), msg.to_string()))?;
|
|
148
148
|
|
|
149
|
-
let
|
|
149
|
+
let compiled = world
|
|
150
150
|
.compile(Some("png"), ppi, &Vec::new(), false, render_bleed)
|
|
151
151
|
.map_err(|msg| magnus::Error::new(ruby.exception_arg_error(), msg.to_string()))?;
|
|
152
152
|
|
|
153
|
-
Ok(
|
|
153
|
+
Ok(compiled)
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
fn to_pdf(
|
|
@@ -163,7 +163,7 @@ fn to_pdf(
|
|
|
163
163
|
pretty: bool,
|
|
164
164
|
sys_inputs: HashMap<String, String>,
|
|
165
165
|
pdf_standards: Vec<String>,
|
|
166
|
-
) -> Result<Vec<Vec<u8>>, Error> {
|
|
166
|
+
) -> Result<(Vec<Vec<u8>>, Vec<String>), Error> {
|
|
167
167
|
let input = input.canonicalize()
|
|
168
168
|
.map_err(|err| magnus::Error::new(ruby.exception_arg_error(), err.to_string()))?;
|
|
169
169
|
|
|
@@ -217,11 +217,11 @@ fn to_pdf(
|
|
|
217
217
|
}
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
-
let
|
|
220
|
+
let compiled = world
|
|
221
221
|
.compile(Some("pdf"), None, &pdf_standards_vec, pretty, true)
|
|
222
222
|
.map_err(|msg| magnus::Error::new(ruby.exception_arg_error(), msg.to_string()))?;
|
|
223
223
|
|
|
224
|
-
Ok(
|
|
224
|
+
Ok(compiled)
|
|
225
225
|
}
|
|
226
226
|
|
|
227
227
|
fn query(
|
|
@@ -287,6 +287,10 @@ fn clear_cache(_ruby: &Ruby, max_age: usize) {
|
|
|
287
287
|
comemo::evict(max_age);
|
|
288
288
|
}
|
|
289
289
|
|
|
290
|
+
fn clear_font_cache(_ruby: &Ruby) {
|
|
291
|
+
world::clear_font_cache();
|
|
292
|
+
}
|
|
293
|
+
|
|
290
294
|
#[magnus::init]
|
|
291
295
|
fn init(ruby: &Ruby) -> Result<(), Error> {
|
|
292
296
|
env_logger::init();
|
|
@@ -298,5 +302,6 @@ fn init(ruby: &Ruby) -> Result<(), Error> {
|
|
|
298
302
|
module.define_singleton_method("_to_html", function!(to_html, 8))?;
|
|
299
303
|
module.define_singleton_method("_query", function!(query, 10))?;
|
|
300
304
|
module.define_singleton_method("_clear_cache", function!(clear_cache, 1))?;
|
|
305
|
+
module.define_singleton_method("_clear_font_cache", function!(clear_font_cache, 0))?;
|
|
301
306
|
Ok(())
|
|
302
307
|
}
|
data/ext/typst/src/world.rs
CHANGED
|
@@ -7,10 +7,10 @@ use chrono::{DateTime, Datelike, FixedOffset, Local};
|
|
|
7
7
|
use typst::diag::{FileError, FileResult, StrResult};
|
|
8
8
|
use typst::foundations::{Bytes, Datetime, Dict, Duration};
|
|
9
9
|
use typst::syntax::{FileId, Lines, Source, VirtualPath, VirtualRoot, RootedPath};
|
|
10
|
-
use typst::text::{Font, FontBook};
|
|
10
|
+
use typst::text::{Font, FontBook, FontInfo};
|
|
11
11
|
use typst::utils::LazyHash;
|
|
12
12
|
use typst::{Features, Library, LibraryExt, World};
|
|
13
|
-
use typst_kit::fonts::{self, FontStore};
|
|
13
|
+
use typst_kit::fonts::{self, FontPath, FontStore};
|
|
14
14
|
use typst_kit::packages::{FsPackages, SystemPackages, UniversePackages};
|
|
15
15
|
|
|
16
16
|
/// A world that provides access to the operating system.
|
|
@@ -195,7 +195,7 @@ impl SystemWorldBuilder {
|
|
|
195
195
|
}
|
|
196
196
|
|
|
197
197
|
pub fn build(self) -> StrResult<SystemWorld> {
|
|
198
|
-
let fonts =
|
|
198
|
+
let fonts = font_store(self.ignore_system_fonts, self.ignore_embedded_fonts, self.font_paths);
|
|
199
199
|
|
|
200
200
|
let package_storage = system_packages(self.package_path, self.package_cache_path);
|
|
201
201
|
|
|
@@ -217,13 +217,84 @@ impl SystemWorldBuilder {
|
|
|
217
217
|
}
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
+
/// Font discovery, which is far too slow to repeat on every compile.
|
|
221
|
+
///
|
|
222
|
+
/// A full system font scan takes about 95 ms, which for a small document
|
|
223
|
+
/// dwarfs the compilation itself. `FontStore` cannot be cloned, so the
|
|
224
|
+
/// discovered fonts are kept alongside the assembled stores.
|
|
225
|
+
struct FontCache {
|
|
226
|
+
/// The locations found by walking the system font directories.
|
|
227
|
+
system: Option<Arc<Vec<(PathBuf, u32, FontInfo)>>>,
|
|
228
|
+
/// The fonts shipped with typst, already parsed.
|
|
229
|
+
embedded: Option<Arc<Vec<(Font, FontInfo)>>>,
|
|
230
|
+
/// A store per combination of the two ignore flags, for compiles that
|
|
231
|
+
/// bring no font paths of their own. Sharing one keeps the fonts a
|
|
232
|
+
/// document uses loaded between compiles and hashes the book only once.
|
|
233
|
+
stores: [Option<Arc<FontStore>>; 4],
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const EMPTY_FONT_CACHE: FontCache =
|
|
237
|
+
FontCache { system: None, embedded: None, stores: [const { None }; 4] };
|
|
238
|
+
|
|
239
|
+
static FONT_CACHE: Mutex<FontCache> = Mutex::new(EMPTY_FONT_CACHE);
|
|
240
|
+
|
|
241
|
+
/// Forgets everything discovered so far, so that the next compile picks up
|
|
242
|
+
/// fonts installed or removed since.
|
|
243
|
+
pub fn clear_font_cache() {
|
|
244
|
+
*FONT_CACHE.lock().unwrap() = EMPTY_FONT_CACHE;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/// Reads a cache field, filling it on a miss.
|
|
248
|
+
///
|
|
249
|
+
/// The lock is not held while building, so two threads missing at once both
|
|
250
|
+
/// build and one result is dropped. That wastes a scan but keeps a 95 ms
|
|
251
|
+
/// build off the lock.
|
|
252
|
+
fn cached<T>(
|
|
253
|
+
select: impl Fn(&mut FontCache) -> &mut Option<Arc<T>>,
|
|
254
|
+
build: impl FnOnce() -> T,
|
|
255
|
+
) -> Arc<T> {
|
|
256
|
+
let hit = select(&mut FONT_CACHE.lock().unwrap()).clone();
|
|
257
|
+
if let Some(value) = hit {
|
|
258
|
+
return value;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
let value = Arc::new(build());
|
|
262
|
+
*select(&mut FONT_CACHE.lock().unwrap()) = Some(value.clone());
|
|
263
|
+
value
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/// Obtains a font store for the given configuration.
|
|
267
|
+
///
|
|
268
|
+
/// Extra font paths are scanned on every compile. They are usually a
|
|
269
|
+
/// temporary directory written by `Typst.build_world_from_s`, so their
|
|
270
|
+
/// contents can change between compiles and caching on them would grow
|
|
271
|
+
/// without bound.
|
|
272
|
+
fn font_store(ignore_system_fonts: bool, ignore_embedded_fonts: bool, font_paths: Vec<PathBuf>) -> Arc<FontStore> {
|
|
273
|
+
if !font_paths.is_empty() {
|
|
274
|
+
return Arc::new(build_font_store(ignore_system_fonts, ignore_embedded_fonts, font_paths));
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
let index = usize::from(ignore_system_fonts) << 1 | usize::from(ignore_embedded_fonts);
|
|
278
|
+
cached(
|
|
279
|
+
move |cache| &mut cache.stores[index],
|
|
280
|
+
|| build_font_store(ignore_system_fonts, ignore_embedded_fonts, Vec::new()),
|
|
281
|
+
)
|
|
282
|
+
}
|
|
283
|
+
|
|
220
284
|
fn build_font_store(ignore_system_fonts: bool, ignore_embedded_fonts: bool, font_paths: Vec<PathBuf>) -> FontStore {
|
|
221
285
|
let mut fonts = FontStore::new();
|
|
222
286
|
if !ignore_system_fonts {
|
|
223
|
-
|
|
287
|
+
let system = cached(
|
|
288
|
+
|cache| &mut cache.system,
|
|
289
|
+
|| fonts::system().map(|(path, info)| (path.path, path.index, info)).collect(),
|
|
290
|
+
);
|
|
291
|
+
fonts.extend(system.iter().map(|(path, index, info)| {
|
|
292
|
+
(FontPath { path: path.clone(), index: *index }, info.clone())
|
|
293
|
+
}));
|
|
224
294
|
}
|
|
225
295
|
if !ignore_embedded_fonts {
|
|
226
|
-
|
|
296
|
+
let embedded = cached(|cache| &mut cache.embedded, || fonts::embedded().collect());
|
|
297
|
+
fonts.extend(embedded.iter().cloned());
|
|
227
298
|
}
|
|
228
299
|
for path in font_paths {
|
|
229
300
|
fonts.extend(fonts::scan(&path));
|
data/lib/document.rb
CHANGED
|
@@ -2,8 +2,18 @@ module Typst
|
|
|
2
2
|
class Document
|
|
3
3
|
attr_accessor :bytes
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
# Diagnostics the compiler emitted while *succeeding*. An unknown font
|
|
6
|
+
# family is the common one: Typst substitutes another face, the document
|
|
7
|
+
# generates, and nothing tells you unless you look here.
|
|
8
|
+
attr_accessor :warnings
|
|
9
|
+
|
|
10
|
+
def initialize(bytes, warnings = [])
|
|
6
11
|
@bytes = bytes
|
|
12
|
+
@warnings = warnings
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def warnings?
|
|
16
|
+
!warnings.empty?
|
|
7
17
|
end
|
|
8
18
|
|
|
9
19
|
def write_some(filename)
|
|
@@ -2,7 +2,8 @@ module Typst
|
|
|
2
2
|
class HtmlExperimental < Base
|
|
3
3
|
def initialize(*options)
|
|
4
4
|
super(*options)
|
|
5
|
-
|
|
5
|
+
bytes, warnings = Typst::_to_html(*self.typst_pretty_args)
|
|
6
|
+
@compiled = HtmlExperimentalDocument.new(bytes, warnings)
|
|
6
7
|
end
|
|
7
8
|
end
|
|
8
9
|
class HtmlExperimentalDocument < Document
|
data/lib/formats/pdf.rb
CHANGED
|
@@ -2,7 +2,8 @@ module Typst
|
|
|
2
2
|
class Pdf < Base
|
|
3
3
|
def initialize(*options)
|
|
4
4
|
super(*options)
|
|
5
|
-
|
|
5
|
+
bytes, warnings = Typst::_to_pdf(*self.typst_pdf_args)
|
|
6
|
+
@compiled = PdfDocument.new(bytes, warnings)
|
|
6
7
|
end
|
|
7
8
|
end
|
|
8
9
|
class PdfDocument < Document
|
data/lib/formats/png.rb
CHANGED
|
@@ -2,7 +2,8 @@ module Typst
|
|
|
2
2
|
class Png < Base
|
|
3
3
|
def initialize(*options)
|
|
4
4
|
super(*options)
|
|
5
|
-
|
|
5
|
+
bytes, warnings = Typst::_to_png(*self.typst_png_args)
|
|
6
|
+
@compiled = PngDocument.new(bytes, warnings)
|
|
6
7
|
end
|
|
7
8
|
end
|
|
8
9
|
class PngDocument < Document
|
data/lib/formats/svg.rb
CHANGED
|
@@ -2,7 +2,8 @@ module Typst
|
|
|
2
2
|
class Svg < Base
|
|
3
3
|
def initialize(*options)
|
|
4
4
|
super(*options)
|
|
5
|
-
|
|
5
|
+
bytes, warnings = Typst::_to_svg(*self.typst_pretty_args)
|
|
6
|
+
@compiled = SvgDocument.new(bytes, warnings)
|
|
6
7
|
end
|
|
7
8
|
end
|
|
8
9
|
class SvgDocument < Document
|
data/lib/typst.rb
CHANGED
|
@@ -17,6 +17,12 @@ module Typst
|
|
|
17
17
|
Typst::_clear_cache(max_age)
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
+
# Discards the discovered system and embedded fonts, so that the next
|
|
21
|
+
# compile picks up fonts installed or removed since the first one.
|
|
22
|
+
def self.clear_font_cache
|
|
23
|
+
Typst::_clear_font_cache
|
|
24
|
+
end
|
|
25
|
+
|
|
20
26
|
def self.build_world_from_s(main_source, **options, &blk)
|
|
21
27
|
dependencies = options[:dependencies] ||= {}
|
|
22
28
|
fonts = options[:fonts] ||= {}
|
|
@@ -30,16 +36,18 @@ module Typst
|
|
|
30
36
|
File.binwrite(tmp_dep_file, dep_source)
|
|
31
37
|
end
|
|
32
38
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
39
|
+
unless fonts.empty?
|
|
40
|
+
relative_font_path = Pathname.new(tmp_dir).join("fonts")
|
|
41
|
+
relative_font_path.mkpath
|
|
42
|
+
fonts.each do |font_name, font_bytes|
|
|
43
|
+
tmp_font_file = relative_font_path.join(font_name)
|
|
44
|
+
File.binwrite(tmp_font_file, font_bytes)
|
|
45
|
+
end
|
|
46
|
+
options[:font_paths] = (options[:font_paths] || []) + [relative_font_path]
|
|
38
47
|
end
|
|
39
48
|
|
|
40
49
|
options[:file] = tmp_main_file
|
|
41
50
|
options[:root] = tmp_dir
|
|
42
|
-
options[:font_paths] = [relative_font_path]
|
|
43
51
|
|
|
44
52
|
blk.call(options)
|
|
45
53
|
end
|
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.15.1.
|
|
4
|
+
version: 0.15.1.8
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Flinn
|
|
@@ -117,7 +117,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
117
117
|
- !ruby/object:Gem::Version
|
|
118
118
|
version: '0'
|
|
119
119
|
requirements: []
|
|
120
|
-
rubygems_version: 4.0.
|
|
120
|
+
rubygems_version: 4.0.20
|
|
121
121
|
specification_version: 4
|
|
122
122
|
summary: Ruby binding to typst, a new markup-based typesetting system that is powerful
|
|
123
123
|
and easy to learn.
|