typst 0.0.1 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
- - ">="
|