typst 0.13.1 → 0.13.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: 880dbd16bacd894d910728407dbc9465317f64ab4c7c20993e0882764fd75019
4
- data.tar.gz: 6d446dfd48e83e560dd889cd9d5f67ce66cb42356dad3697324135daf66a1ecc
3
+ metadata.gz: efba2668a9abea7b8c3679ce34f67d35c29737f1c2c3afd88c8542b7609ec4e8
4
+ data.tar.gz: 285d1408b40eafc2237089c7b5bfcd32196f832ce4b1e05e5175a2cde5558ae8
5
5
  SHA512:
6
- metadata.gz: 9c8045304a0a676621f3f2384d1ffed27e5222bc29a9c0e20d5257806e0158b1130630ba7663fcf41d6def7e103e8cd5faa9092349eee3bb82043f286a7715bf
7
- data.tar.gz: 8adb1272c9725712007cc80199d156b61fdc91d3ef8df95076595c4be5855b55269991e235ac439102f05a9ffb826e87a3d8593d25e198cbada7845d3a6962d0
6
+ metadata.gz: c15d656ce6673c9e1c8d67f5743c8c13a1ea3e8a960d93271f27ec8a4e41144f505593f37ec81bf97a7f608b123a7fa57fbac4aada94a5bdc2b06369eb09f607
7
+ data.tar.gz: 9c080fa1092d04481e1a78c99e871d53af7138025a0586d2993bb9d8f43167f7eee2c742d266478011172d4a62150e6589723c191080bdc2daedfec20be1dd70
data/README.md CHANGED
@@ -14,59 +14,75 @@ gem install typst
14
14
  ```ruby
15
15
  require "typst"
16
16
 
17
- # Compile `readme.typ` to PDF and save as `readme.pdf`
18
- Typst::Pdf.new("readme.typ").write("readme.pdf")
17
+ # Compile a typst file and write the output to a PDF file
18
+ Typst("readme.typ").compile(:pdf).write("readme.pdf")
19
19
 
20
- # Or return PDF content as an array of bytes
21
- pdf_bytes = Typst::Pdf.new("readme.typ").bytes
22
- # => [37, 80, 68, 70, 45, 49, 46, 55, 10, 37, 128 ...]
20
+ # Use a typst file `readme.typ`
21
+ t = Typst("readme.typ")
23
22
 
24
- # Or return PDF content as a string of bytes
25
- document = Typst::Pdf.new("readme.typ").document
26
- # => "%PDF-1.7\n%\x80\x80\x80\x80\n\n4 0 obj\n<<\n /Type /Font\n /Subtype ..."
23
+ # Use a typst string
24
+ t = Typst(body: %{hello world})
27
25
 
28
- # Compile `readme.typ` to SVG and save as `readme_0.svg`, `readme_1.svg`
29
- Typst::Svg.new("readme.typ").write("readme.svg")
26
+ # Use a typst file in a zip file
27
+ t = Typst(zip: "test/main.typ.zip")
30
28
 
31
- # Or return SVG content as an array of pages
32
- pages = Typst::Svg.new("readme.typ").pages
33
- # => ["<svg class=\"typst-doc\" viewBox=\"0 0 595.2764999999999 841.89105\" ..."
29
+ # Compile to PDF
30
+ f = t.compile(:pdf)
34
31
 
35
- # Compile `readme.typ` to PNG and save as `readme_0.png`, `readme_1.png`
36
- Typst::Png.new("readme.typ").write("readme.png")
32
+ # Compile to SVG
33
+ f = t.compile(:svg)
37
34
 
38
- # Or return PNG content as an array of pages
39
- pages = Typst::Png.new("readme.typ").pages
40
- # => ["\x89PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x04\xA7\x00\x00\x06\x94\b\ ...
35
+ # Compile to PNG
36
+ f = t.compile(:png)
41
37
 
42
- # Compile `readme.typ` to SVG and save as `readme.html`
43
- Typst::Html.new("readme.typ", title: "README").write("readme.html")
38
+ # Compile to SVGs enveloped in HTML
39
+ # Depracation warning: this feature will go away once Typst HTML moves out of experimental
40
+ f = t.compile(:html, title: "Typst+Ruby")
44
41
 
45
- # Or return HTML content
46
- markup = Typst::Html.new("readme.typ", title: "README").document
47
- # => "\n<!DOCTYPE html>\n<html>\n<head>\n<title>README</title>\n</head>\n<bo..."
42
+ # Compile to HTML (using Typst expirmental HTML)
43
+ f = t.compile(:html_experimental)
48
44
 
49
- # Use native Typst experimental HTML feature to write single frame HTML file
50
- Typst::HtmlExperimental.new("readme.typ").write("readme.html")
45
+ # Access PDF or HTML output as a string
46
+ # Note: For PDF and PNG this will give data, for SVG and HTML this will give markup
47
+ Typst("readme.typ").compile(:pdf).document
48
+ # => "%PDF-1.7\n%\x80\x80\x80\x80\n\n4 0 obj\n<<\n /Type /Font\n /Subtype ..."
49
+ Typst("readme.typ").compile(:html).document
50
+ # => "\n<!DOCTYPE html>\n<html>\n<head>\n<title>main</title>\n</head>\n<body>\n<svg class=\"typst-doc\" ...
51
51
 
52
- # Or return single frame HTML content (using native Typst experimental HTML feature)
53
- markup = Typst::HtmlExperimental.new("readme.typ").document
54
- # => "<!DOCTYPE html>\n<html>\n <head>\n <meta charset=\"utf-8\">\n..."
52
+ # Or return content as an array of bytes
53
+ pdf_bytes = Typst("readme.typ").compile(:pdf).bytes
54
+ # => [37, 80, 68, 70, 45, 49, 46, 55, 10, 37, 128 ...]
55
55
 
56
- # Compile from a string to PDF
57
- t = Typst::Pdf.from_s(%{hello world})
56
+ # Write the output to a file
57
+ # Note: for multi-page documents using formats other than PDF, pages write to multiple files, e.g. `readme_0.png`, `readme_1.png`
58
+ f.write("filename.pdf")
58
59
 
59
- # Compile from a string to SVG
60
- t = Typst::Svg.from_s(%{hello world})
60
+ # Return SVG, HTML or PNG content as an array of pages
61
+ Typst("readme.typ").compile(:svg).pages
62
+ # => ["<svg class=\"typst-doc\" viewBox=\"0 0 595.2764999999999 841.89105\" ..."
63
+ Typst("readme.typ").compile(:html).pages
64
+ # => ["<svg class=\"typst-doc\" viewBox=\"0 0 595.2764999999999 841.89105\" ..."
65
+ Typst("readme.typ").compile(:png).pages
66
+ # => ["\x89PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x04\xA7\x00\x00\x06\x94\b\ ...
67
+
68
+ # Pass values into typst using sys_inputs
69
+ sys_inputs_example = %{
70
+ #let persons = json(bytes(sys.inputs.persons))
61
71
 
62
- # Compile from a string to PNG
63
- t = Typst::Png.from_s(%{hello world})
72
+ #for person in persons [
73
+ #person.name is #person.age years old.\\
74
+ ]
75
+ }
76
+ people = [{"name" => "John", "age" => 35}, {"name" => "Xoliswa", "age" => 45}]
77
+ data = { "persons" => people.to_json }
78
+ Typst(body: sys_inputs_example, sys_inputs: data).compile(:pdf).write("sys_inputs_example.pdf")
64
79
 
65
- # Compile from a string to SVG multi-frame/pages wrapped in HTML (non-native Typst)
66
- t = Typst::Html.from_s(%{hello world})
80
+ # Apply inputs to typst to product multiple PDFs
67
81
 
68
- # Compile from a string to single frame HTML (native Typst experimental feature)
69
- t = Typst::HtmlExperimental.from_s(%{hello world})
82
+ t = Typst(body: sys_inputs_example)
83
+ people.each do |person|
84
+ t.with_inputs({ "persons" => [person].to_json }).compile(:pdf).write("#{person['name']}.pdf")
85
+ end
70
86
 
71
87
  # A more complex example of compiling from string
72
88
  main = %{
@@ -89,14 +105,10 @@ template = %{
89
105
  icon = File.read("icon.svg")
90
106
  font_bytes = File.read("Example.ttf")
91
107
 
92
- t = Typst::Pdf.from_s(main, dependencies: { "template.typ" => template, "icon.svg" => icon }, fonts: { "Example.ttf" => font_bytes })
93
-
94
- # From a zip file that includes a main.typ
95
- # zip file include flat dependencies included and a fonts directory
96
- Typst::Pdf::from_zip("working_directory.zip")
97
-
108
+ Typst(body: main, dependencies: { "template.typ" => template, "icon.svg" => icon }, fonts: { "Example.ttf" => font_bytes }).compile(:pdf)
109
+
98
110
  # From a zip with a named main typst file
99
- Typst::Pdf::from_zip("working_directory.zip", "hello.typ")
111
+ Typst(zip: "test/main.typ.zip", main_file: "hello.typ").compile(:pdf)
100
112
 
101
113
  Typst::Query.new("heading", "readme.typ").result
102
114
  # =>
data/README.typ CHANGED
@@ -17,59 +17,75 @@ gem install typst
17
17
  ```ruby
18
18
  require "typst"
19
19
 
20
- # Compile `readme.typ` to PDF and save as `readme.pdf`
21
- Typst::Pdf.new("readme.typ").write("readme.pdf")
20
+ # Compile a typst file and write the output to a PDF file
21
+ Typst("readme.typ").compile(:pdf).write("readme.pdf")
22
22
 
23
- # Or return PDF content as an array of bytes
24
- pdf_bytes = Typst::Pdf.new("readme.typ").bytes
25
- # => [37, 80, 68, 70, 45, 49, 46, 55, 10, 37, 128 ...]
23
+ # Use a typst file `readme.typ`
24
+ t = Typst("readme.typ")
26
25
 
27
- # Or return PDF content as a string of bytes
28
- document = Typst::Pdf.new("readme.typ").document
29
- # => "%PDF-1.7\n%\x80\x80\x80\x80\n\n4 0 obj\n<<\n /Type /Font\n /Subtype ..."
26
+ # Use a typst string
27
+ t = Typst(body: %{hello world})
30
28
 
31
- # Compile `readme.typ` to SVG and save as `readme_0.svg`, `readme_1.svg`
32
- Typst::Svg.new("readme.typ").write("readme.svg")
29
+ # Use a typst file in a zip file
30
+ t = Typst(zip: "test/main.typ.zip")
33
31
 
34
- # Or return SVG content as an array of pages
35
- pages = Typst::Svg.new("readme.typ").pages
36
- # => ["<svg class=\"typst-doc\" viewBox=\"0 0 595.2764999999999 841.89105\" ..."
32
+ # Compile to PDF
33
+ f = t.compile(:pdf)
37
34
 
38
- # Compile `readme.typ` to PNG and save as `readme_0.png`, `readme_1.png`
39
- Typst::Png.new("readme.typ").write("readme.png")
35
+ # Compile to SVG
36
+ f = t.compile(:svg)
40
37
 
41
- # Or return PNG content as an array of pages
42
- pages = Typst::Png.new("readme.typ").pages
43
- # => ["\x89PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x04\xA7\x00\x00\x06\x94\b\ ...
38
+ # Compile to PNG
39
+ f = t.compile(:png)
44
40
 
45
- # Compile `readme.typ` to SVG and save as `readme.html`
46
- Typst::Html.new("readme.typ", title: "README").write("readme.html")
41
+ # Compile to SVGs enveloped in HTML
42
+ # Depracation warning: this feature will go away once Typst HTML moves out of experimental
43
+ f = t.compile(:html, title: "Typst+Ruby")
47
44
 
48
- # Or return HTML content
49
- markup = Typst::Html.new("readme.typ", title: "README").document
50
- # => "\n<!DOCTYPE html>\n<html>\n<head>\n<title>README</title>\n</head>\n<bo..."
45
+ # Compile to HTML (using Typst expirmental HTML)
46
+ f = t.compile(:html_experimental)
51
47
 
52
- # Use native Typst experimental HTML feature to write single frame HTML file
53
- Typst::HtmlExperimental.new("readme.typ").write("readme.html")
48
+ # Access PDF or HTML output as a string
49
+ # Note: For PDF and PNG this will give data, for SVG and HTML this will give markup
50
+ Typst("readme.typ").compile(:pdf).document
51
+ # => "%PDF-1.7\n%\x80\x80\x80\x80\n\n4 0 obj\n<<\n /Type /Font\n /Subtype ..."
52
+ Typst("readme.typ").compile(:html).document
53
+ # => "\n<!DOCTYPE html>\n<html>\n<head>\n<title>main</title>\n</head>\n<body>\n<svg class=\"typst-doc\" ...
54
54
 
55
- # Or return single frame HTML content (using native Typst experimental HTML feature)
56
- markup = Typst::HtmlExperimental.new("readme.typ").document
57
- # => "<!DOCTYPE html>\n<html>\n <head>\n <meta charset=\"utf-8\">\n..."
55
+ # Or return content as an array of bytes
56
+ pdf_bytes = Typst("readme.typ").compile(:pdf).bytes
57
+ # => [37, 80, 68, 70, 45, 49, 46, 55, 10, 37, 128 ...]
58
58
 
59
- # Compile from a string to PDF
60
- t = Typst::Pdf.from_s(%{hello world})
59
+ # Write the output to a file
60
+ # Note: for multi-page documents using formats other than PDF, pages write to multiple files, e.g. `readme_0.png`, `readme_1.png`
61
+ f.write("filename.pdf")
61
62
 
62
- # Compile from a string to SVG
63
- t = Typst::Svg.from_s(%{hello world})
63
+ # Return SVG, HTML or PNG content as an array of pages
64
+ Typst("readme.typ").compile(:svg).pages
65
+ # => ["<svg class=\"typst-doc\" viewBox=\"0 0 595.2764999999999 841.89105\" ..."
66
+ Typst("readme.typ").compile(:html).pages
67
+ # => ["<svg class=\"typst-doc\" viewBox=\"0 0 595.2764999999999 841.89105\" ..."
68
+ Typst("readme.typ").compile(:png).pages
69
+ # => ["\x89PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x04\xA7\x00\x00\x06\x94\b\ ...
70
+
71
+ # Pass values into typst using sys_inputs
72
+ sys_inputs_example = %{
73
+ #let persons = json(bytes(sys.inputs.persons))
64
74
 
65
- # Compile from a string to PNG
66
- t = Typst::Png.from_s(%{hello world})
75
+ #for person in persons [
76
+ #person.name is #person.age years old.\\
77
+ ]
78
+ }
79
+ people = [{"name" => "John", "age" => 35}, {"name" => "Xoliswa", "age" => 45}]
80
+ data = { "persons" => people.to_json }
81
+ Typst(body: sys_inputs_example, sys_inputs: data).compile(:pdf).write("sys_inputs_example.pdf")
67
82
 
68
- # Compile from a string to SVG multi-frame/pages wrapped in HTML (non-native Typst)
69
- t = Typst::Html.from_s(%{hello world})
83
+ # Apply inputs to typst to product multiple PDFs
70
84
 
71
- # Compile from a string to single frame HTML (native Typst experimental feature)
72
- t = Typst::HtmlExperimental.from_s(%{hello world})
85
+ t = Typst(body: sys_inputs_example)
86
+ people.each do |person|
87
+ t.with_inputs({ "persons" => [person].to_json }).compile(:pdf).write("#{person['name']}.pdf")
88
+ end
73
89
 
74
90
  # A more complex example of compiling from string
75
91
  main = %{
@@ -92,14 +108,10 @@ template = %{
92
108
  icon = File.read("icon.svg")
93
109
  font_bytes = File.read("Example.ttf")
94
110
 
95
- t = Typst::Pdf.from_s(main, dependencies: { "template.typ" => template, "icon.svg" => icon }, fonts: { "Example.ttf" => font_bytes })
96
-
97
- # From a zip file that includes a main.typ
98
- # zip file include flat dependencies included and a fonts directory
99
- Typst::Pdf::from_zip("working_directory.zip")
100
-
111
+ Typst(body: main, dependencies: { "template.typ" => template, "icon.svg" => icon }, fonts: { "Example.ttf" => font_bytes }).compile(:pdf)
112
+
101
113
  # From a zip with a named main typst file
102
- Typst::Pdf::from_zip("working_directory.zip", "hello.typ")
114
+ Typst(zip: "test/main.typ.zip", main_file: "hello.typ").compile(:pdf)
103
115
 
104
116
  Typst::Query.new("heading", "readme.typ").result
105
117
  # =>
data/lib/base.rb ADDED
@@ -0,0 +1,169 @@
1
+ module Typst
2
+ class Base
3
+ attr_accessor :options
4
+ attr_accessor :compiled
5
+
6
+ def initialize(*options)
7
+ if options.size.zero?
8
+ raise "No options given"
9
+ elsif options.first.is_a?(String)
10
+ file, options = options
11
+ options ||= {}
12
+ options[:file] = file
13
+ elsif options.first.is_a?(Hash)
14
+ options = options.first
15
+ end
16
+
17
+ if options.has_key?(:file)
18
+ raise "Can't find file" unless File.exist?(options[:file])
19
+ elsif options.has_key?(:body)
20
+ raise "Empty body" if options[:body].to_s.empty?
21
+ elsif options.has_key?(:zip)
22
+ raise "Can't find zip" unless File.exist?(options[:zip])
23
+ else
24
+ raise "No input given"
25
+ end
26
+
27
+ root = Pathname.new(options[:root] || ".").expand_path
28
+ raise "Invalid path for root" unless root.exist?
29
+ options[:root] = root.to_s
30
+
31
+ font_paths = (options[:font_paths] || []).collect{ |fp| Pathname.new(fp).expand_path }
32
+ options[:font_paths] = font_paths.collect(&:to_s)
33
+
34
+ options[:dependencies] ||= {}
35
+ options[:fonts] ||= {}
36
+ options[:sys_inputs] ||= {}
37
+
38
+ self.options = options
39
+ end
40
+
41
+ def typst_args
42
+ [options[:file], options[:root], options[:font_paths], File.dirname(__FILE__), false, options[:sys_inputs].map{ |k,v| [k.to_s,v.to_s] }.to_h]
43
+ end
44
+
45
+ def self.from_s(main_source, **options)
46
+ dependencies = options[:dependencies] ||= {}
47
+ fonts = options[:fonts] ||= {}
48
+
49
+ Dir.mktmpdir do |tmp_dir|
50
+ tmp_main_file = Pathname.new(tmp_dir).join("main.typ")
51
+ File.write(tmp_main_file, main_source)
52
+
53
+ dependencies.each do |dep_name, dep_source|
54
+ tmp_dep_file = Pathname.new(tmp_dir).join(dep_name)
55
+ File.write(tmp_dep_file, dep_source)
56
+ end
57
+
58
+ relative_font_path = Pathname.new(tmp_dir).join("fonts")
59
+ fonts.each do |font_name, font_bytes|
60
+ Pathname.new(relative_font_path).mkpath
61
+ tmp_font_file = relative_font_path.join(font_name)
62
+ File.write(tmp_font_file, font_bytes)
63
+ end
64
+
65
+ options[:file] = tmp_main_file
66
+ options[:root] = tmp_dir
67
+ options[:font_paths] = [relative_font_path]
68
+
69
+ if options[:format]
70
+ Typst::formats[options[:format]].new(**options)
71
+ else
72
+ new(**options)
73
+ end
74
+ end
75
+ end
76
+
77
+ def self.from_zip(zip_file_path, main_file = nil, **options)
78
+ options[:dependencies] ||= {}
79
+ options[:fonts] ||= {}
80
+
81
+ Zip::File.open(zip_file_path) do |zipfile|
82
+ file_names = zipfile.dir.glob("*").collect{ |f| f.name }
83
+ case
84
+ when file_names.include?(main_file) then tmp_main_file = main_file
85
+ when file_names.include?("main.typ") then tmp_main_file = "main.typ"
86
+ when file_names.size == 1 then tmp_main_file = file_names.first
87
+ else raise "no main file found"
88
+ end
89
+ main_source = zipfile.file.read(tmp_main_file)
90
+ file_names.delete(tmp_main_file)
91
+ file_names.delete("fonts/")
92
+
93
+ file_names.each do |dep_name|
94
+ options[:dependencies][dep_name] = zipfile.file.read(dep_name)
95
+ end
96
+
97
+ font_file_names = zipfile.dir.glob("fonts/*").collect{ |f| f.name }
98
+ font_file_names.each do |font_name|
99
+ options[:fonts][Pathname.new(font_name).basename.to_s] = zipfile.file.read(font_name)
100
+ end
101
+
102
+ options[:main_file] = tmp_main_file
103
+
104
+ from_s(main_source, **options)
105
+ end
106
+ end
107
+
108
+ def with_dependencies(dependencies)
109
+ self.options[:dependencies] = self.options[:dependencies].merge(dependencies)
110
+ self
111
+ end
112
+
113
+ def with_fonts(fonts)
114
+ self.options[:fonts] = self.options[:fonts].merge(fonts)
115
+ self
116
+ end
117
+
118
+ def with_inputs(inputs)
119
+ self.options[:sys_inputs] = self.options[:sys_inputs].merge(inputs)
120
+ self
121
+ end
122
+
123
+ def with_font_paths(font_paths)
124
+ self.options[:font_paths] = self.options[:font_paths] + font_paths
125
+ self
126
+ end
127
+
128
+ def with_root(root)
129
+ self.options[:root] = root
130
+ self
131
+ end
132
+
133
+ def compile(format, **options)
134
+ raise "Invalid format" if Typst::formats[format].nil?
135
+
136
+ options = self.options.merge(options)
137
+
138
+ if options.has_key?(:file)
139
+ Typst::formats[format].new(**options).compiled
140
+ elsif options.has_key?(:body)
141
+ Typst::formats[format].from_s(options[:body], **options).compiled
142
+ elsif options.has_key?(:zip)
143
+ Typst::formats[format].from_zip(options[:zip], options[:main_file], **options).compiled
144
+ else
145
+ raise "No input given"
146
+ end
147
+ end
148
+
149
+ def write(output)
150
+ STDERR.puts "DEPRECATION WARNING: this method will go away in a future version"
151
+ compiled.write(output)
152
+ end
153
+
154
+ def document
155
+ STDERR.puts "DEPRECATION WARNING: this method will go away in a future version"
156
+ compiled.document
157
+ end
158
+
159
+ def bytes
160
+ STDERR.puts "DEPRECATION WARNING: this method will go away in a future version"
161
+ compiled.bytes
162
+ end
163
+
164
+ def pages
165
+ STDERR.puts "DEPRECATION WARNING: this method will go away in a future version"
166
+ compiled.pages
167
+ end
168
+ end
169
+ end
data/lib/document.rb ADDED
@@ -0,0 +1,29 @@
1
+ module Typst
2
+ class Document
3
+ attr_accessor :bytes
4
+
5
+ def initialize(bytes)
6
+ @bytes = bytes
7
+ end
8
+
9
+ def write(out)
10
+ if pages.size == 1
11
+ File.write(out, pages.first, mode: "wb")
12
+ else
13
+ pages.each_with_index do |page, i|
14
+ fn = File.basename(out, ".*") + "_{{n}}" + File.extname(out) unless out.include?("{{n}}")
15
+ fn = fn.gsub("{{n}}", (i+1).to_s)
16
+ File.write(fn, page, mode: "wb")
17
+ end
18
+ end
19
+ end
20
+
21
+ def pages
22
+ bytes.collect{ |page| page.pack("C*").to_s }
23
+ end
24
+
25
+ def document
26
+ pages.size == 1 ? pages.first : pages
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,35 @@
1
+ module Typst
2
+ class Html < Base
3
+ def initialize(*options)
4
+ super(*options)
5
+ title = CGI::escapeHTML(@options[:title] || File.basename(@options[:file], ".*"))
6
+ @compiled = HtmlDocument.new(Typst::_to_svg(*self.typst_args), title)
7
+ end
8
+ end
9
+
10
+ class HtmlDocument < Document
11
+ attr_accessor :title
12
+
13
+ def initialize(bytes, title)
14
+ super(bytes)
15
+ self.title = title
16
+ end
17
+
18
+ def markup
19
+ %{
20
+ <!DOCTYPE html>
21
+ <html>
22
+ <head>
23
+ <title>#{title}</title>
24
+ </head>
25
+ <body>
26
+ #{pages.join("<br />")}
27
+ </body>
28
+ </html>
29
+ }
30
+ end
31
+ alias_method :document, :markup
32
+ end
33
+
34
+ register_format(html: Html)
35
+ end
@@ -0,0 +1,11 @@
1
+ module Typst
2
+ class HtmlExperimental < Base
3
+ def initialize(*options)
4
+ super(*options)
5
+ @compiled = HtmlExperimentalDocument.new(Typst::_to_html(*self.typst_args))
6
+ end
7
+ end
8
+ class HtmlExperimentalDocument < Document; end
9
+
10
+ register_format(html_experimental: HtmlExperimental)
11
+ end
@@ -0,0 +1,11 @@
1
+ module Typst
2
+ class Pdf < Base
3
+ def initialize(*options)
4
+ super(*options)
5
+ @compiled = PdfDocument.new(Typst::_to_pdf(*self.typst_args))
6
+ end
7
+ end
8
+ class PdfDocument < Document; end
9
+
10
+ register_format(pdf: Pdf)
11
+ end
@@ -0,0 +1,11 @@
1
+ module Typst
2
+ class Png < Base
3
+ def initialize(*options)
4
+ super(*options)
5
+ @compiled = PngDocument.new(Typst::_to_png(*self.typst_args))
6
+ end
7
+ end
8
+ class PngDocument < Document; end
9
+
10
+ register_format(png: Png)
11
+ end
@@ -0,0 +1,11 @@
1
+ module Typst
2
+ class Svg < Base
3
+ def initialize(*options)
4
+ super(*options)
5
+ @compiled = SvgDocument.new(Typst::_to_svg(*self.typst_args))
6
+ end
7
+ end
8
+ class SvgDocument < Document; end
9
+
10
+ register_format(svg: Svg)
11
+ end
data/lib/query.rb ADDED
@@ -0,0 +1,19 @@
1
+ module Typst
2
+ class Query < Base
3
+ attr_accessor :format
4
+
5
+ def initialize(selector, input, field: nil, one: false, format: "json", root: ".", font_paths: [], sys_inputs: {})
6
+ super(input, root: root, font_paths: font_paths, sys_inputs: sys_inputs)
7
+ self.format = format
8
+ @result = Typst::_query(selector, field, one, format, input, root, font_paths, File.dirname(__FILE__), false, sys_inputs)
9
+ end
10
+
11
+ def result(raw: false)
12
+ case raw || format
13
+ when "json" then JSON(@result)
14
+ when "yaml" then YAML::safe_load(@result)
15
+ else @result
16
+ end
17
+ end
18
+ end
19
+ end
data/lib/typst.rb CHANGED
@@ -1,201 +1,31 @@
1
- require_relative "typst/typst"
2
- require "cgi"
3
- require "pathname"
4
- require "tmpdir"
5
- require "zip/filesystem"
1
+ def Typst(*options)
2
+ Typst::Base.new(*options)
3
+ end
6
4
 
7
5
  module Typst
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, "wb"){ |f| f.write(document) }
21
- end
22
-
23
- def self.from_s(main_source, dependencies: {}, fonts: {})
24
- dependencies = {} if dependencies.nil?
25
- fonts = {} if fonts.nil?
26
- Dir.mktmpdir do |tmp_dir|
27
- tmp_main_file = Pathname.new(tmp_dir).join("main.typ")
28
- File.write(tmp_main_file, main_source)
29
-
30
- dependencies.each do |dep_name, dep_source|
31
- tmp_dep_file = Pathname.new(tmp_dir).join(dep_name)
32
- File.write(tmp_dep_file, dep_source)
33
- end
34
-
35
- relative_font_path = Pathname.new(tmp_dir).join("fonts")
36
- fonts.each do |font_name, font_bytes|
37
- Pathname.new(relative_font_path).mkpath
38
- tmp_font_file = relative_font_path.join(font_name)
39
- File.write(tmp_font_file, font_bytes)
40
- end
41
-
42
- new(tmp_main_file, root: tmp_dir, font_paths: [relative_font_path])
43
- end
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(main_source, dependencies: dependencies, fonts: fonts)
72
- end
73
- end
74
- end
75
-
76
- class Pdf < Base
77
- attr_accessor :bytes
78
-
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__), false, {})[0]
82
- end
83
-
84
- def document
85
- bytes.pack("C*").to_s
86
- end
87
- end
88
-
89
- class Svg < Base
90
- attr_accessor :pages
91
-
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__), false, {}).collect{ |page| page.pack("C*").to_s }
95
- end
96
-
97
- def write(output)
98
- if pages.size > 1
99
- pages.each_with_index do |page, i|
100
- if output.include?("{{n}}")
101
- file_name = output.gsub("{{n}}", (i+1).to_s)
102
- else
103
- file_name = File.basename(output, File.extname(output)) + "_" + i.to_s
104
- file_name = file_name + File.extname(output)
105
- end
106
- File.open(file_name, "w"){ |f| f.write(page) }
107
- end
108
- elsif pages.size == 1
109
- File.open(output, "w"){ |f| f.write(pages[0]) }
110
- else
111
- end
112
- end
113
- end
114
-
115
- class Png < Base
116
- attr_accessor :pages
6
+ @@formats = {}
117
7
 
118
- def initialize(input, root: ".", font_paths: [])
119
- super(input, root: root, font_paths: font_paths)
120
- @pages = Typst::_to_png(self.input, self.root, self.font_paths, File.dirname(__FILE__), false, {}).collect{ |page| page.pack("C*").to_s }
121
- end
122
-
123
- def write(output)
124
- if pages.size > 1
125
- pages.each_with_index do |page, i|
126
- if output.include?("{{n}}")
127
- file_name = output.gsub("{{n}}", (i+1).to_s)
128
- else
129
- file_name = File.basename(output, File.extname(output)) + "_" + i.to_s
130
- file_name = file_name + File.extname(output)
131
- end
132
- File.open(file_name, "w"){ |f| f.write(page) }
133
- end
134
- elsif pages.size == 1
135
- File.open(output, "w"){ |f| f.write(pages[0]) }
136
- else
137
- end
138
- end
8
+ def self.register_format(**format)
9
+ @@formats.merge!(format)
139
10
  end
140
-
141
- class Html < Base
142
- attr_accessor :title
143
- attr_accessor :svg
144
- attr_accessor :html
145
-
146
- def initialize(input, title: nil, root: ".", font_paths: [])
147
- super(input, root: root, font_paths: font_paths)
148
- title = title || File.basename(input, File.extname(input))
149
- self.title = CGI::escapeHTML(title)
150
- self.svg = Svg.new(self.input, root: self.root, font_paths: self.font_paths)
151
- end
152
11
 
153
- def markup
154
- %{
155
- <!DOCTYPE html>
156
- <html>
157
- <head>
158
- <title>#{title}</title>
159
- </head>
160
- <body>
161
- #{svg.pages.join("<br />")}
162
- </body>
163
- </html>
164
- }
165
- end
166
- alias_method :document, :markup
167
- end
168
-
169
- class HtmlExperimental < Base
170
- attr_accessor :bytes
171
-
172
- def initialize(input, root: ".", font_paths: [])
173
- super(input, root: root, font_paths: font_paths)
174
- @bytes = Typst::_to_html(self.input, self.root, self.font_paths, File.dirname(__FILE__), false, {})[0]
175
- end
176
-
177
- def document
178
- bytes.pack("C*").to_s
179
- end
180
- alias_method :markup, :document
12
+ def self.formats
13
+ @@formats
181
14
  end
15
+ end
182
16
 
183
- class Query < Base
184
- attr_accessor :format
185
- attr_accessor :result
186
17
 
187
- def initialize(selector, input, field: nil, one: false, format: "json", root: ".", font_paths: [])
188
- super(input, root: root, font_paths: font_paths)
189
- self.format = format
190
- self.result = Typst::_query(selector, field, one, format, self.input, self.root, self.font_paths, File.dirname(__FILE__), false, {})
191
- end
18
+ require "cgi"
19
+ require "pathname"
20
+ require "tmpdir"
21
+ require "zip/filesystem"
192
22
 
193
- def result(raw: false)
194
- case raw || format
195
- when "json" then JSON(@result)
196
- when "yaml" then YAML::safe_load(@result)
197
- else @result
198
- end
199
- end
200
- end
201
- end
23
+ require_relative "typst/typst"
24
+ require_relative "base"
25
+ require_relative "query"
26
+ require_relative "document"
27
+ require_relative "formats/pdf"
28
+ require_relative "formats/svg"
29
+ require_relative "formats/png"
30
+ require_relative "formats/html"
31
+ require_relative "formats/html_experimental"
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: typst
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.1
4
+ version: 0.13.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Flinn
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-02-27 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rb_sys
@@ -37,6 +37,20 @@ dependencies:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
39
  version: '2.4'
40
+ - !ruby/object:Gem::Dependency
41
+ name: hexapdf
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
40
54
  email: flinn@actsasflinn.com
41
55
  executables: []
42
56
  extensions:
@@ -57,6 +71,8 @@ files:
57
71
  - ext/typst/src/package.rs
58
72
  - ext/typst/src/query.rs
59
73
  - ext/typst/src/world.rs
74
+ - lib/base.rb
75
+ - lib/document.rb
60
76
  - lib/fonts/DejaVuSansMono-Bold.ttf
61
77
  - lib/fonts/DejaVuSansMono-BoldOblique.ttf
62
78
  - lib/fonts/DejaVuSansMono-Oblique.ttf
@@ -71,6 +87,12 @@ files:
71
87
  - lib/fonts/NewCM10-Regular.otf
72
88
  - lib/fonts/NewCMMath-Book.otf
73
89
  - lib/fonts/NewCMMath-Regular.otf
90
+ - lib/formats/html.rb
91
+ - lib/formats/html_experimental.rb
92
+ - lib/formats/pdf.rb
93
+ - lib/formats/png.rb
94
+ - lib/formats/svg.rb
95
+ - lib/query.rb
74
96
  - lib/typst.rb
75
97
  homepage: https://github.com/actsasflinn/typst-rb
76
98
  licenses:
@@ -90,7 +112,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
90
112
  - !ruby/object:Gem::Version
91
113
  version: '0'
92
114
  requirements: []
93
- rubygems_version: 3.6.5
115
+ rubygems_version: 3.6.8
94
116
  specification_version: 4
95
117
  summary: Ruby binding to typst, a new markup-based typesetting system that is powerful
96
118
  and easy to learn.