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 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
  - - ">="