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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 43d8c84a75186f58f150a1b401f023269a87b65ff4181622d12ee0605e44e12c
4
- data.tar.gz: b04f05668004a681b3dbf23cd8c5741e7a5d88418b1b282144b8a855575e9ae1
3
+ metadata.gz: ea8821be2119d9f300be030629e825d14c8fe380cd2c858a875337eb703e7df1
4
+ data.tar.gz: e831657b70680e117e16db19750e9c382a3cff766f7dbe990d1b876c7e898772
5
5
  SHA512:
6
- metadata.gz: 90852c841c355b42fd6f305627d6ce61e3ad2321e56e8a45bab1eff715888f232f76779eb217375e0da4855c24fd139cb14a86dea2a4aa53aeacccc816ece7da
7
- data.tar.gz: 2e661ba34cdef35d3e9911b461cbd06815c31a2ac4408fa6be4a3395519f8a159bf3a3e31ae6cc573f78dbbe14a8818f0089dacf59f6ad6d779e80c79d02733d
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: ["fonts"])
38
- self.input = input
39
- self.root = 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: ["fonts"])
61
- self.input = input
62
- self.root = 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 = nil, root: ".", font_paths: ["fonts"])
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
- @title = CGI::escapeHTML(title)
94
- @svg = Svg.new(input, root: root, font_paths: font_paths)
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.1
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-26 00:00:00.000000000 Z
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.2.2
75
+ version: 3.0.0
75
76
  required_rubygems_version: !ruby/object:Gem::Requirement
76
77
  requirements:
77
78
  - - ">="