typst 0.0.1 → 0.0.3
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 +8 -1
- data/README.typ +8 -1
- data/Rakefile +7 -0
- data/ext/typst/src/lib.wip.rs +203 -0
- data/lib/typst.rb +58 -31
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ea8821be2119d9f300be030629e825d14c8fe380cd2c858a875337eb703e7df1
|
4
|
+
data.tar.gz: e831657b70680e117e16db19750e9c382a3cff766f7dbe990d1b876c7e898772
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e14bdb9e0275a8d716e966442d776e1d514ee055cf473f3795e84555a51e09a30afb124aa2e0ed82be1f3ada4aac1b83a48825e346e3b3b08b4eb7134a3a15ff
|
7
|
+
data.tar.gz: 66c4dfa603503ce492690718f4d7aa74f5ba23db0492752f53e0ebe02892ae3bdbe028f694bb363584e387612cb19b4d3d5cab85f49761105975758bb92c29e6
|
data/README.md
CHANGED
@@ -36,7 +36,7 @@ pages = Typst::Svg.new("readme.typ").pages
|
|
36
36
|
Typst::Html.new("readme.typ", "README").write("readme.html")
|
37
37
|
|
38
38
|
# Or return HTML content
|
39
|
-
markup = Typst::Html.new("readme.typ", "README").markup
|
39
|
+
markup = Typst::Html.new("readme.typ", title: "README").markup
|
40
40
|
# => "\n<!DOCTYPE html>\n<html>\n<head>\n<title>README</title>\n</head>\n<bo..."
|
41
41
|
|
42
42
|
# Compile from a string to PDF
|
@@ -70,6 +70,13 @@ icon = File.read("icon.svg")
|
|
70
70
|
font_bytes = File.read("Example.ttf")
|
71
71
|
|
72
72
|
t = Typst::Pdf.from_s(main, dependencies: { "template.typ" => template, "icon.svg" => icon }, fonts: { "Example.ttf" => font_bytes })
|
73
|
+
|
74
|
+
# From a zip file that includes a main.typ
|
75
|
+
# zip file include flat dependencies included and a fonts directory
|
76
|
+
Typst::Pdf::from_zip("working_directory.zip")
|
77
|
+
|
78
|
+
# From a zip with a named main typst file
|
79
|
+
Typst::Pdf::from_zip("working_directory.zip", "hello.typ")
|
73
80
|
```
|
74
81
|
|
75
82
|
## Contributors & Acknowledgements
|
data/README.typ
CHANGED
@@ -39,7 +39,7 @@ pages = Typst::Svg.new("readme.typ").pages
|
|
39
39
|
Typst::Html.new("readme.typ", "README").write("readme.html")
|
40
40
|
|
41
41
|
# Or return HTML content
|
42
|
-
markup = Typst::Html.new("readme.typ", "README").markup
|
42
|
+
markup = Typst::Html.new("readme.typ", title: "README").markup
|
43
43
|
# => "\n<!DOCTYPE html>\n<html>\n<head>\n<title>README</title>\n</head>\n<bo..."
|
44
44
|
|
45
45
|
# Compile from a string to PDF
|
@@ -73,6 +73,13 @@ icon = File.read("icon.svg")
|
|
73
73
|
font_bytes = File.read("Example.ttf")
|
74
74
|
|
75
75
|
t = Typst::Pdf.from_s(main, dependencies: { "template.typ" => template, "icon.svg" => icon }, fonts: { "Example.ttf" => font_bytes })
|
76
|
+
|
77
|
+
# From a zip file that includes a main.typ
|
78
|
+
# zip file include flat dependencies included and a fonts directory
|
79
|
+
Typst::Pdf::from_zip("working_directory.zip")
|
80
|
+
|
81
|
+
# From a zip with a named main typst file
|
82
|
+
Typst::Pdf::from_zip("working_directory.zip", "hello.typ")
|
76
83
|
```
|
77
84
|
|
78
85
|
== Contributors & Acknowledgements
|
data/Rakefile
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
2
|
require "rake/extensiontask"
|
3
|
+
require "rake/testtask"
|
3
4
|
require "rubygems/package_task"
|
4
5
|
require "bundler"
|
5
6
|
|
@@ -22,3 +23,9 @@ Rake::ExtensionTask.new("typst", spec) do |ext|
|
|
22
23
|
ext.cross_compile = true
|
23
24
|
ext.cross_platform = CROSS_PLATFORMS
|
24
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,203 @@
|
|
1
|
+
use std::path::PathBuf;
|
2
|
+
use std::env;
|
3
|
+
|
4
|
+
use std::{
|
5
|
+
cell::RefCell,
|
6
|
+
fmt,
|
7
|
+
hash::{Hash, Hasher},
|
8
|
+
};
|
9
|
+
|
10
|
+
use magnus::r_string::IntoRString;
|
11
|
+
//use magnus::{function, exception, Error, IntoValue};
|
12
|
+
use magnus::{
|
13
|
+
class, define_class, exception, method, module, function,
|
14
|
+
prelude::*,
|
15
|
+
scan_args::{get_kwargs, scan_args},
|
16
|
+
typed_data, error::Error, Value, IntoValue, Symbol
|
17
|
+
};
|
18
|
+
|
19
|
+
use world::SystemWorld;
|
20
|
+
|
21
|
+
mod compiler;
|
22
|
+
mod download;
|
23
|
+
mod fonts;
|
24
|
+
mod package;
|
25
|
+
mod world;
|
26
|
+
|
27
|
+
fn compile(
|
28
|
+
input: PathBuf,
|
29
|
+
output: Option<PathBuf>,
|
30
|
+
root: Option<PathBuf>,
|
31
|
+
font_paths: Vec<PathBuf>,
|
32
|
+
) -> Result<magnus::Value, Error> {
|
33
|
+
let input = input.canonicalize()
|
34
|
+
.map_err(|err| magnus::Error::new(exception::arg_error(), err.to_string()))?;
|
35
|
+
|
36
|
+
let root = if let Some(root) = root {
|
37
|
+
root.canonicalize()
|
38
|
+
.map_err(|err| magnus::Error::new(exception::arg_error(), err.to_string()))?
|
39
|
+
} else if let Some(dir) = input.parent() {
|
40
|
+
dir.into()
|
41
|
+
} else {
|
42
|
+
PathBuf::new()
|
43
|
+
};
|
44
|
+
|
45
|
+
let resource_path = env::current_dir()
|
46
|
+
.map_err(|err| magnus::Error::new(exception::arg_error(), err.to_string()))?;
|
47
|
+
|
48
|
+
let mut default_fonts = Vec::new();
|
49
|
+
for entry in walkdir::WalkDir::new(resource_path.join("fonts")) {
|
50
|
+
let path = entry
|
51
|
+
.map_err(|err| magnus::Error::new(exception::arg_error(), err.to_string()))?
|
52
|
+
.into_path();
|
53
|
+
let Some(extension) = path.extension() else {
|
54
|
+
continue;
|
55
|
+
};
|
56
|
+
if extension == "ttf" || extension == "otf" {
|
57
|
+
default_fonts.push(path);
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
let mut world = SystemWorld::builder(root, input)
|
62
|
+
.font_paths(font_paths)
|
63
|
+
.font_files(default_fonts)
|
64
|
+
.build()
|
65
|
+
.map_err(|msg| magnus::Error::new(exception::arg_error(), msg.to_string()))?;
|
66
|
+
|
67
|
+
let pdf_bytes = world
|
68
|
+
.compile()
|
69
|
+
.map_err(|msg| magnus::Error::new(exception::arg_error(), msg.to_string()))?;
|
70
|
+
|
71
|
+
if let Some(output) = output {
|
72
|
+
std::fs::write(output, pdf_bytes)
|
73
|
+
.map_err(|_| magnus::Error::new(exception::arg_error(), "error"))?;
|
74
|
+
|
75
|
+
let value = true.into_value();
|
76
|
+
Ok(value)
|
77
|
+
} else {
|
78
|
+
let value = pdf_bytes.into_value();
|
79
|
+
Ok(value)
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
#[magnus::wrap(class = "Typst", free_immediately, size)]
|
84
|
+
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd)]
|
85
|
+
struct Typst {
|
86
|
+
input: PathBuf,
|
87
|
+
output: Option<PathBuf>,
|
88
|
+
root: PathBuf,
|
89
|
+
font_paths: Vec<PathBuf>,
|
90
|
+
}
|
91
|
+
|
92
|
+
// can't derive this due to needing to use RefCell to get mutability
|
93
|
+
impl Hash for Typst {
|
94
|
+
fn hash<H: Hasher>(&self, state: &mut H) {
|
95
|
+
self.input.hash(state)
|
96
|
+
}
|
97
|
+
}
|
98
|
+
|
99
|
+
//#[magnus::wrap(class = "Typst")]
|
100
|
+
impl Typst {
|
101
|
+
fn initialize(rb_self: typed_data::Obj<Self>, args: &[Value]) -> Result<Value, Error> {
|
102
|
+
let args = scan_args::<_, _, (), (), _, ()>(args)?;
|
103
|
+
let (input,): (PathBuf,) = args.required;
|
104
|
+
let (output,): (
|
105
|
+
Option<PathBuf>,
|
106
|
+
) = args.optional;
|
107
|
+
|
108
|
+
let kw = get_kwargs::<_, (), (Option<PathBuf>, Option<Vec<PathBuf>>), ()>(args.keywords, &[], &["root", "font_paths"])?;
|
109
|
+
let (root, font_paths) = kw.optional;
|
110
|
+
|
111
|
+
//*rb_self.input.borrow_mut() = input;
|
112
|
+
//*rb_self.output.borrow_mut() = output;
|
113
|
+
rb_self.input = input;
|
114
|
+
rb_self.output = output;
|
115
|
+
|
116
|
+
rb_self.root = if let Some(root) = root {
|
117
|
+
root
|
118
|
+
} else {
|
119
|
+
PathBuf::new()
|
120
|
+
};
|
121
|
+
|
122
|
+
rb_self.font_paths = if let Some(font_paths) = font_paths {
|
123
|
+
font_paths
|
124
|
+
} else {
|
125
|
+
let font_paths:Vec<PathBuf> = Vec::new();
|
126
|
+
font_paths
|
127
|
+
};
|
128
|
+
|
129
|
+
Ok(rb_self.into_value())
|
130
|
+
}
|
131
|
+
|
132
|
+
fn compile(rb_self: typed_data::Obj<Self>, args: &[Value]) -> Result<Value, Error> {
|
133
|
+
let resource_path = env::current_dir()
|
134
|
+
.map_err(|err| magnus::Error::new(exception::arg_error(), err.to_string()))?;
|
135
|
+
|
136
|
+
let mut default_fonts = Vec::new();
|
137
|
+
for entry in walkdir::WalkDir::new(resource_path.join("fonts")) {
|
138
|
+
let path = entry
|
139
|
+
.map_err(|err| magnus::Error::new(exception::arg_error(), err.to_string()))?
|
140
|
+
.into_path();
|
141
|
+
let Some(extension) = path.extension() else {
|
142
|
+
continue;
|
143
|
+
};
|
144
|
+
if extension == "ttf" || extension == "otf" {
|
145
|
+
default_fonts.push(path);
|
146
|
+
}
|
147
|
+
}
|
148
|
+
|
149
|
+
//let input = PathBuf::from(*rb_self.input.borrow());
|
150
|
+
//let root = PathBuf::from(*rb_self.root.borrow());
|
151
|
+
let input = PathBuf::from(&rb_self.input);
|
152
|
+
let root = PathBuf::from(&rb_self.root);
|
153
|
+
let font_paths: Vec<PathBuf> = Vec::new();
|
154
|
+
let mut world = SystemWorld::builder(root, input)
|
155
|
+
.font_paths(font_paths)
|
156
|
+
.font_files(default_fonts)
|
157
|
+
.build()
|
158
|
+
.map_err(|msg| magnus::Error::new(exception::arg_error(), msg.to_string()))?;
|
159
|
+
|
160
|
+
let pdf_bytes = world
|
161
|
+
.compile()
|
162
|
+
.map_err(|msg| magnus::Error::new(exception::arg_error(), msg.to_string()))?;
|
163
|
+
|
164
|
+
//let op: Option<PathBuf> = *rb_self.output.borrow();
|
165
|
+
//if let Some(output) = op {
|
166
|
+
// std::fs::write(output, pdf_bytes)
|
167
|
+
// .map_err(|_| magnus::Error::new(exception::arg_error(), "error"))?;
|
168
|
+
|
169
|
+
// let value = true.into_value();
|
170
|
+
// Ok(value)
|
171
|
+
//} else {
|
172
|
+
let value = pdf_bytes.into_value();
|
173
|
+
Ok(value)
|
174
|
+
//}
|
175
|
+
}
|
176
|
+
}
|
177
|
+
|
178
|
+
impl fmt::Display for Typst {
|
179
|
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
180
|
+
write!(f, "{}", self.input.display())
|
181
|
+
}
|
182
|
+
}
|
183
|
+
|
184
|
+
#[magnus::init]
|
185
|
+
fn init() -> Result<(), Error> {
|
186
|
+
let class = define_class("Typst", class::object())?;
|
187
|
+
|
188
|
+
// Define alloc func based on the Default impl, plus an initialize method,
|
189
|
+
// rather than overwriting `new`, to allow class to be subclassed from Ruby
|
190
|
+
class.define_alloc_func::<Typst>();
|
191
|
+
class.define_method("initialize", method!(Typst::initialize, -1))?;
|
192
|
+
|
193
|
+
//class.define_singleton_method("foo", function!(foo, -1))?;
|
194
|
+
|
195
|
+
class.define_method("inspect",method!(<Typst as typed_data::Inspect>::inspect, 0))?;
|
196
|
+
|
197
|
+
class.define_method("to_s", method!(Typst::to_string, 0))?;
|
198
|
+
// class.define_method("barf", method!(Typst::barf, 0))?;
|
199
|
+
|
200
|
+
//let module = magnus::define_module("Typst").unwrap();
|
201
|
+
//module.define_module_function("compile", function!(compile, 4)).unwrap();
|
202
|
+
Ok(())
|
203
|
+
}
|
data/lib/typst.rb
CHANGED
@@ -2,10 +2,27 @@ require_relative "typst/typst"
|
|
2
2
|
require "cgi"
|
3
3
|
require "pathname"
|
4
4
|
require "tmpdir"
|
5
|
+
require "zip/filesystem"
|
5
6
|
|
6
7
|
module Typst
|
7
8
|
class Base
|
9
|
+
attr_accessor :input
|
10
|
+
attr_accessor :root
|
11
|
+
attr_accessor :font_paths
|
12
|
+
|
13
|
+
def initialize(input, root: ".", font_paths: [])
|
14
|
+
self.input = input
|
15
|
+
self.root = Pathname.new(root).expand_path.to_s
|
16
|
+
self.font_paths = font_paths.collect{ |fp| Pathname.new(fp).expand_path.to_s }
|
17
|
+
end
|
18
|
+
|
19
|
+
def write(output)
|
20
|
+
File.open(output, "w"){ |f| f.write(document) }
|
21
|
+
end
|
22
|
+
|
8
23
|
def self.from_s(main_source, dependencies: {}, fonts: {})
|
24
|
+
dependencies = {} if dependencies.nil?
|
25
|
+
fonts = {} if fonts.nil?
|
9
26
|
Dir.mktmpdir do |tmp_dir|
|
10
27
|
tmp_main_file = Pathname.new(tmp_dir).join("main.typ")
|
11
28
|
File.write(tmp_main_file, main_source)
|
@@ -16,7 +33,6 @@ module Typst
|
|
16
33
|
end
|
17
34
|
|
18
35
|
relative_font_path = Pathname.new(tmp_dir).join("fonts")
|
19
|
-
puts fonts
|
20
36
|
fonts.each do |font_name, font_bytes|
|
21
37
|
Pathname.new(relative_font_path).mkpath
|
22
38
|
tmp_font_file = relative_font_path.join(font_name)
|
@@ -26,43 +42,56 @@ module Typst
|
|
26
42
|
new(tmp_main_file, root: tmp_dir, font_paths: [relative_font_path])
|
27
43
|
end
|
28
44
|
end
|
45
|
+
|
46
|
+
def self.from_zip(zip_file_path, main_file = nil)
|
47
|
+
dependencies = {}
|
48
|
+
fonts = {}
|
49
|
+
|
50
|
+
Zip::File.open(zip_file_path) do |zipfile|
|
51
|
+
file_names = zipfile.dir.glob("*").collect{ |f| f.name }
|
52
|
+
case
|
53
|
+
when file_names.include?(main_file) then tmp_main_file = main_file
|
54
|
+
when file_names.include?("main.typ") then tmp_main_file = "main.typ"
|
55
|
+
when file_names.size == 1 then tmp_main_file = file_names.first
|
56
|
+
else raise "no main file found"
|
57
|
+
end
|
58
|
+
main_source = zipfile.file.read(tmp_main_file)
|
59
|
+
file_names.delete(tmp_main_file)
|
60
|
+
file_names.delete("fonts/")
|
61
|
+
|
62
|
+
file_names.each do |dep_name|
|
63
|
+
dependencies[dep_name] = zipfile.file.read(dep_name)
|
64
|
+
end
|
65
|
+
|
66
|
+
font_file_names = zipfile.dir.glob("fonts/*").collect{ |f| f.name }
|
67
|
+
font_file_names.each do |font_name|
|
68
|
+
fonts[Pathname.new(font_name).basename.to_s] = zipfile.file.read(font_name)
|
69
|
+
end
|
70
|
+
|
71
|
+
from_s(tmp_main_file, dependencies: dependencies, fonts: fonts)
|
72
|
+
end
|
73
|
+
end
|
29
74
|
end
|
30
75
|
|
31
76
|
class Pdf < Base
|
32
|
-
attr_accessor :input
|
33
|
-
attr_accessor :root
|
34
|
-
attr_accessor :font_paths
|
35
77
|
attr_accessor :bytes
|
36
78
|
|
37
|
-
def initialize(input, root: ".", font_paths: [
|
38
|
-
|
39
|
-
self.root
|
40
|
-
self.font_paths = font_paths
|
41
|
-
|
42
|
-
@bytes = Typst::_to_pdf(input, root, font_paths, File.dirname(__FILE__))
|
79
|
+
def initialize(input, root: ".", font_paths: [])
|
80
|
+
super(input, root: root, font_paths: font_paths)
|
81
|
+
@bytes = Typst::_to_pdf(self.input, self.root, self.font_paths, File.dirname(__FILE__))
|
43
82
|
end
|
44
83
|
|
45
84
|
def document
|
46
85
|
bytes.pack("C*").to_s
|
47
86
|
end
|
48
|
-
|
49
|
-
def write(output)
|
50
|
-
File.open(output, "w"){ |f| f.write(document) }
|
51
|
-
end
|
52
87
|
end
|
53
88
|
|
54
89
|
class Svg < Base
|
55
|
-
attr_accessor :input
|
56
|
-
attr_accessor :root
|
57
|
-
attr_accessor :font_paths
|
58
90
|
attr_accessor :pages
|
59
91
|
|
60
|
-
def initialize(input, root: ".", font_paths: [
|
61
|
-
|
62
|
-
self.root
|
63
|
-
self.font_paths = font_paths
|
64
|
-
|
65
|
-
@pages = Typst::_to_svg(input, root, font_paths, File.dirname(__FILE__))
|
92
|
+
def initialize(input, root: ".", font_paths: [])
|
93
|
+
super(input, root: root, font_paths: font_paths)
|
94
|
+
@pages = Typst::_to_svg(self.input, self.root, self.font_paths, File.dirname(__FILE__))
|
66
95
|
end
|
67
96
|
|
68
97
|
def write(output)
|
@@ -88,12 +117,13 @@ module Typst
|
|
88
117
|
attr_accessor :svg
|
89
118
|
attr_accessor :html
|
90
119
|
|
91
|
-
def initialize(input, title
|
120
|
+
def initialize(input, title: nil, root: ".", font_paths: [])
|
121
|
+
super(input, root: root, font_paths: font_paths)
|
92
122
|
title = title || File.basename(input, File.extname(input))
|
93
|
-
|
94
|
-
|
123
|
+
self.title = CGI::escapeHTML(title)
|
124
|
+
self.svg = Svg.new(self.input, root: self.root, font_paths: self.font_paths)
|
95
125
|
end
|
96
|
-
|
126
|
+
|
97
127
|
def markup
|
98
128
|
%{
|
99
129
|
<!DOCTYPE html>
|
@@ -107,9 +137,6 @@ module Typst
|
|
107
137
|
</html>
|
108
138
|
}
|
109
139
|
end
|
110
|
-
|
111
|
-
def write(output)
|
112
|
-
File.open(output, "w"){ |f| f.write(markup) }
|
113
|
-
end
|
140
|
+
alias_method :document, :markup
|
114
141
|
end
|
115
142
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: typst
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Flinn
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-11-
|
11
|
+
date: 2023-11-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rb_sys
|
@@ -42,6 +42,7 @@ files:
|
|
42
42
|
- ext/typst/src/download.rs
|
43
43
|
- ext/typst/src/fonts.rs
|
44
44
|
- ext/typst/src/lib.rs
|
45
|
+
- ext/typst/src/lib.wip.rs
|
45
46
|
- ext/typst/src/package.rs
|
46
47
|
- ext/typst/src/world.rs
|
47
48
|
- lib/fonts/DejaVuSansMono-Bold.ttf
|
@@ -71,7 +72,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
71
72
|
requirements:
|
72
73
|
- - ">="
|
73
74
|
- !ruby/object:Gem::Version
|
74
|
-
version: 3.
|
75
|
+
version: 3.0.0
|
75
76
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
77
|
requirements:
|
77
78
|
- - ">="
|