asciidoctor-tex2svg 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 11832f1a28faf8f6cc791ee9b1733934729453b1e7f72a7a32730bf02d59094b
4
+ data.tar.gz: 70e260040e415b280aa19f85c0be33816214b2d87b8abfd3489ab4e14320e43b
5
+ SHA512:
6
+ metadata.gz: af39b1f79aa60ca89bc3178199eb0c82dafc3bbc2f8f3fec572f5b615629fa99a97701c920d7c5795ec98d262d0b99edaa57b708a044b44cc781a60aba357505
7
+ data.tar.gz: de853f522e38172ab5a0696a0cefdfb02aac4160acb60acb61c67d4bf48f9896511a1694623c8e3115ea5e1c415b26f2c68c60980b57e13b23cf5c2d86fd44b6
@@ -0,0 +1,5 @@
1
+ require_relative 'asciidoctor-tex2svg/extension'
2
+
3
+ Asciidoctor::Extensions.register do
4
+ treeprocessor Tex2SvgTreeprocessor
5
+ end
@@ -0,0 +1,222 @@
1
+ require 'pathname'
2
+ require 'asciidoctor/extensions'
3
+ require 'asciimath'
4
+ require 'open3'
5
+
6
+ autoload :Digest, 'digest'
7
+
8
+ class Tex2SvgTreeprocessor < Asciidoctor::Extensions::Treeprocessor
9
+ LineFeed = %(\n)
10
+ StemInlineMacroRx = /\\?(?:stem|latexmath|asciimath):([a-z,]*)\[(.*?[^\\])\]/m
11
+ LatexmathInlineMacroRx = /\\?latexmath:([a-z,]*)\[(.*?[^\\])\]/m
12
+ AsciiMathInlineMacroRx = /\\?asciimath:([a-z,]*)\[(.*?[^\\])\]/m
13
+
14
+ def process document
15
+ inline = document.attr 'tex2svg-inline'
16
+
17
+ unless inline
18
+ image_output_dir, image_target_dir = image_output_and_target_dir document
19
+ ::Asciidoctor::Helpers.mkdir_p image_output_dir unless ::File.directory? image_output_dir
20
+ end
21
+
22
+ unless (stem_blocks = document.find_by context: :stem).nil_or_empty?
23
+ stem_blocks.each do |stem|
24
+ handle_stem_block stem, image_output_dir, image_target_dir, inline
25
+ end
26
+ end
27
+
28
+ unless (prose_blocks = document.find_by {|b|
29
+ (b.content_model == :simple && (b.subs.include? :macros)) || b.context == :list_item
30
+ }).nil_or_empty?
31
+ prose_blocks.each do |prose|
32
+ handle_prose_block prose, image_output_dir, image_target_dir, inline
33
+ end
34
+ end
35
+
36
+ unless (table_blocks = document.find_by context: :table).nil_or_empty?
37
+ table_blocks.each do |table|
38
+ (table.rows[:body] + table.rows[:foot]).each do |row|
39
+ row.each do |cell|
40
+ if cell.style == :asciidoc
41
+ process cell.inner_document
42
+ elsif cell.style != :literal
43
+ handle_nonasciidoc_table_cell cell, image_output_dir, image_target_dir, inline
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ unless (sect_blocks = document.find_by content: :section).nil_or_empty?
51
+ sect_blocks.each do |sect|
52
+ handle_section_title sect, image_output_dir, image_target_dir, inline
53
+ end
54
+ end
55
+
56
+ nil
57
+ end
58
+
59
+ def handle_stem_block(stem, image_output_dir, image_target_dir, inline)
60
+ equation_type = stem.style.to_sym
61
+
62
+ case equation_type
63
+ when :latexmath
64
+ content = stem.content
65
+ when :asciimath
66
+ content = AsciiMath.parse(stem.content).to_latex
67
+ else
68
+ return
69
+ end
70
+
71
+ img_target = make_equ_image content, stem.id, false, image_output_dir, image_target_dir, inline
72
+
73
+ parent = stem.parent
74
+ if inline
75
+ stem_image = create_pass_block parent, %{<div class="stemblock"> #{img_target} </div>}, {}
76
+ parent.blocks[parent.blocks.index stem] = stem_image
77
+ else
78
+ alt_text = stem.attr 'alt', (equation_type == :latexmath ? %($$#{content}$$) : %(`#{content}`))
79
+ attrs = {'target' => img_target, 'alt' => alt_text, 'align' => 'center'}
80
+ parent = stem.parent
81
+ stem_image = create_image_block parent, attrs
82
+ stem_image.id = stem.id if stem.id
83
+ if (title = stem.attributes['title'])
84
+ stem_image.title = title
85
+ end
86
+ parent.blocks[parent.blocks.index stem] = stem_image
87
+ end
88
+ end
89
+
90
+ def handle_prose_block(prose, image_output_dir, image_target_dir, inline)
91
+ text = prose.context == :list_item ? (prose.instance_variable_get :@text) : (prose.lines * LineFeed)
92
+ text, source_modified = handle_inline_stem prose, text, image_output_dir, image_target_dir, inline
93
+ if source_modified
94
+ if prose.context == :list_item
95
+ prose.instance_variable_set :@text, text
96
+ else
97
+ prose.lines = text.split LineFeed
98
+ end
99
+ end
100
+ end
101
+
102
+ def handle_nonasciidoc_table_cell(cell, image_output_dir, image_target_dir, inline)
103
+ text = cell.instance_variable_get :@text
104
+ text, source_modified = handle_inline_stem cell, text, image_output_dir, image_target_dir, inline
105
+ if source_modified
106
+ cell.instance_variable_set :@text, text
107
+ end
108
+ end
109
+
110
+ def handle_section_title(sect, image_output_dir, image_target_dir, inline)
111
+ text = sect.instance_variable_get :@title
112
+ text, source_modified = handle_inline_stem sect, text, image_output_dir, image_target_dir, inline
113
+ if source_modified
114
+ sect.instance_variable_set :@title, text
115
+ sect.remove_instance_variable :@subbed_title
116
+ end
117
+ end
118
+
119
+ def handle_inline_stem(node, text, image_output_dir, image_target_dir, inline)
120
+ document = node.document
121
+ to_html = document.basebackend? 'html'
122
+
123
+ case document.attr 'stem'
124
+ when 'latexmath'
125
+ support_stem_prefix = true
126
+ stem_rx = LatexmathInlineMacroRx
127
+ when 'asciimath'
128
+ support_stem_prefix = true
129
+ stem_rx = AsciiMathInlineMacroRx
130
+ else
131
+ support_stem_prefix = false
132
+ stem_rx = StemInlineMacroRx
133
+ end
134
+
135
+ source_modified = false
136
+
137
+ # TODO skip passthroughs in the source (e.g., +stem:[x^2]+)
138
+ if text != nil && (text.include? ':')
139
+ text = text.gsub(stem_rx) {
140
+ if (m = $~)[0].start_with? '\\'
141
+ next m[0][1..-1]
142
+ end
143
+
144
+ if (eq_data = m[2].rstrip).empty?
145
+ next
146
+ else
147
+ source_modified = true
148
+ end
149
+
150
+ if text.include? 'asciimath:'
151
+ eq_data = AsciiMath.parse(eq_data).to_latex
152
+ elsif (support_stem_prefix && (text.include? 'stem:')) || (text.include? 'latexmath:')
153
+ eq_data.gsub! '\]', ']'
154
+ subs = m[1].nil_or_empty? ? (to_html ? [:specialcharacters] : []) : (node.resolve_pass_subs m[1])
155
+ eq_data = node.apply_subs eq_data, subs unless subs.empty?
156
+ else
157
+ source_modified = false
158
+ return text
159
+ end
160
+
161
+ img_target = make_equ_image eq_data, nil, true, image_output_dir, image_target_dir, inline
162
+ if inline
163
+ %(pass:[<span class="steminline"> #{img_target} </span>])
164
+ else
165
+ %(image:#{img_target}[])
166
+ end
167
+ }
168
+ end
169
+
170
+ [text, source_modified]
171
+ end
172
+
173
+ def make_equ_image(equ_data, equ_id, equ_inline, image_output_dir, image_target_dir, inline)
174
+ input = equ_data
175
+
176
+ # TODO: consider only making the image if the file isn't already there.
177
+
178
+ if inline
179
+ data, = Open3.capture2('tex2svg', '--inline', input)
180
+ else
181
+ data, = Open3.capture2('tex2svg', input)
182
+ end
183
+
184
+ if inline
185
+ data
186
+ else
187
+ unless equ_id
188
+ equ_id = %(stem-#{::Digest::MD5.hexdigest input})
189
+ end
190
+ image_ext = '.svg'
191
+ img_target = %(#{equ_id}#{image_ext})
192
+ img_file = ::File.join image_output_dir, img_target
193
+
194
+ ::IO.write img_file, data
195
+
196
+ img_target = ::File.join image_target_dir, img_target unless image_target_dir == '.'
197
+ img_target
198
+ end
199
+ end
200
+
201
+ def image_output_and_target_dir(doc)
202
+ output_dir = doc.attr('imagesoutdir')
203
+ if output_dir
204
+ if doc.attr('imagesdir').nil_or_empty?
205
+ target_dir = output_dir
206
+ else
207
+ # When imagesdir attribute is set, every relative path is prefixed with it. So the real target dir shall then be relative to the imagesdir, instead of being relative to document root.
208
+ abs_imagesdir = ::Pathname.new doc.normalize_system_path(doc.attr('imagesdir'))
209
+ abs_outdir = ::Pathname.new doc.normalize_system_path(output_dir)
210
+ target_dir = abs_outdir.relative_path_from(abs_imagesdir).to_s
211
+ end
212
+ else
213
+ output_dir = doc.attr('imagesdir')
214
+ # since we store images directly to imagesdir, target dir shall be NULL and asciidoctor converters will prefix imagesdir.
215
+ target_dir = "."
216
+ end
217
+
218
+ output_dir = doc.normalize_system_path(output_dir, doc.attr('docdir'))
219
+ return [output_dir, target_dir]
220
+ end
221
+
222
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: asciidoctor-tex2svg
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Alex LeBlanc
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-07-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ruby-enum
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: asciidoctor
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 2.0.0
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '2.0'
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 2.0.0
47
+ - !ruby/object:Gem::Dependency
48
+ name: asciimath
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: 2.0.0
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: 2.0.0
57
+ type: :runtime
58
+ prerelease: false
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: 2.0.0
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: 2.0.0
67
+ - !ruby/object:Gem::Dependency
68
+ name: rake
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: 12.3.0
74
+ type: :development
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: 12.3.0
81
+ description: An Asciidoctor extension to converts latexmath equations to SVG
82
+ email: alex.leblanc@nuvation.com
83
+ executables: []
84
+ extensions: []
85
+ extra_rdoc_files: []
86
+ files:
87
+ - lib/asciidoctor-tex2svg.rb
88
+ - lib/asciidoctor-tex2svg/extension.rb
89
+ homepage: https://github.com/dalemartin/asciidoctor-tex2svg
90
+ licenses:
91
+ - MIT
92
+ metadata: {}
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ requirements: []
108
+ rubygems_version: 3.1.2
109
+ signing_key:
110
+ specification_version: 4
111
+ summary: Asciidoctor STEM processor based on MathJax tex2svg
112
+ test_files: []