asciidoctor-pdf-mathjax 0.5.0 → 0.6.0
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 +4 -4
- data/bin/render.js +34 -7
- data/lib/asciidoctor-pdf-mathjax.rb +55 -11
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3fc66d0f0c7026c39d6363d44464ad9c5f430aed3202b344de302604679d3166
|
|
4
|
+
data.tar.gz: 62009314bb1e5e72e759e2251ce7eaa7ffc14bb123a365a9c0f88dcc58d04bac
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5234f95491bf7ec6320942e21c009f77575567a4312b1651e13736f19b1cb85d8eeee8cd9b3e443e483628c0b1e7d18847384cb98e9b4cab5efafb539973bcc8
|
|
7
|
+
data.tar.gz: 7e89244f2911bc889e4f2a3cdd2d34fc31fde201bacac64a0d995ddcbfbd14afe367e98c20922c828ce80bed93e0e05877cd2dbfb81fd655adda92e5c396a6e0
|
data/bin/render.js
CHANGED
|
@@ -2,10 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
const mj = require('mathjax-node');
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
// Two modes:
|
|
6
|
+
// render.js <latex> <format> <pixels_per_ex> <font>
|
|
7
|
+
// renders one formula, prints the SVG and exits.
|
|
8
|
+
// render.js --server <font>
|
|
9
|
+
// starts MathJax once and renders one formula per line of stdin,
|
|
10
|
+
// {"math": "...", "format": "TeX", "ex": 6}, answering with one line
|
|
11
|
+
// per formula, {"svg": "..."} or {"error": "..."}.
|
|
12
|
+
// Starting MathJax takes seconds; the server mode pays that once per font
|
|
13
|
+
// instead of once per formula.
|
|
14
|
+
const server = process.argv[2] === '--server';
|
|
15
|
+
const font = server ? process.argv[3] : process.argv[5];
|
|
9
16
|
|
|
10
17
|
mj.config({
|
|
11
18
|
MathJax: {
|
|
@@ -26,6 +33,26 @@ async function convertToSvg(latex, format, pixels_per_ex) {
|
|
|
26
33
|
return data.svg;
|
|
27
34
|
}
|
|
28
35
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}).
|
|
36
|
+
if (server) {
|
|
37
|
+
let queue = Promise.resolve();
|
|
38
|
+
require('readline').createInterface({ input: process.stdin }).on('line', line => {
|
|
39
|
+
queue = queue.then(async () => {
|
|
40
|
+
let answer;
|
|
41
|
+
try {
|
|
42
|
+
const request = JSON.parse(line);
|
|
43
|
+
answer = { svg: await convertToSvg(request.math, request.format, request.ex) };
|
|
44
|
+
} catch (err) {
|
|
45
|
+
answer = { error: String(err) };
|
|
46
|
+
}
|
|
47
|
+
process.stdout.write(JSON.stringify(answer) + '\n');
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
} else {
|
|
51
|
+
const latex = process.argv[2];
|
|
52
|
+
const format = process.argv[3];
|
|
53
|
+
const pixels_per_ex = parseInt(process.argv[4]);
|
|
54
|
+
|
|
55
|
+
convertToSvg(latex, format, pixels_per_ex).then(svg => {
|
|
56
|
+
console.log(svg);
|
|
57
|
+
}).catch(err => console.error(err));
|
|
58
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require 'asciidoctor-pdf' unless Asciidoctor::Converter.for 'pdf'
|
|
2
2
|
require 'open3'
|
|
3
|
+
require 'json'
|
|
3
4
|
require 'tempfile'
|
|
4
5
|
require 'rexml/document'
|
|
5
6
|
require 'ttfunk'
|
|
@@ -97,7 +98,7 @@ class AsciidoctorPDFExtensions < (Asciidoctor::Converter.for 'pdf')
|
|
|
97
98
|
tmp_svg.close
|
|
98
99
|
|
|
99
100
|
logger.debug "Successfully embedded stem inline #{node.text} with font #{math_font} as SVG image"
|
|
100
|
-
quoted_text = "<img src=\"#{tmp_svg.path}\" format=\"svg\" width=\"#{svg_width}\" alt=\"#{node.text}\">"
|
|
101
|
+
quoted_text = "<img src=\"#{tmp_svg.path}\" format=\"svg\" width=\"#{svg_width}\" alt=\"#{node.text.gsub('"', '"')}\">"
|
|
101
102
|
node.id ? %(<a id="#{node.id}">#{DummyText}</a>#{quoted_text}) : quoted_text
|
|
102
103
|
rescue => e
|
|
103
104
|
logger.warn "Failed to process SVG: #{e.message}"
|
|
@@ -124,19 +125,62 @@ class AsciidoctorPDFExtensions < (Asciidoctor::Converter.for 'pdf')
|
|
|
124
125
|
end
|
|
125
126
|
|
|
126
127
|
def stem_to_svg(latex_content, is_inline, math_font)
|
|
127
|
-
js_script = File.join(File.dirname(__FILE__), '../bin/render.js')
|
|
128
|
-
svg_output, error = nil, nil
|
|
129
128
|
format = is_inline ? 'inline-TeX' : 'TeX'
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
129
|
+
MathJaxServer.for(math_font).render(latex_content, format)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# One long-running render.js process per math font. Starting node and MathJax takes
|
|
133
|
+
# seconds, rendering a formula takes milliseconds, so a document pays the start once.
|
|
134
|
+
class MathJaxServer
|
|
135
|
+
JS_SCRIPT = File.join(File.dirname(__FILE__), '../bin/render.js')
|
|
136
|
+
|
|
137
|
+
@servers = {}
|
|
138
|
+
|
|
139
|
+
def self.for(math_font)
|
|
140
|
+
@servers[math_font] ||= new(math_font)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def initialize(math_font)
|
|
144
|
+
@math_font = math_font
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def render(latex_content, format)
|
|
148
|
+
start unless @stdin
|
|
149
|
+
@stdin.puts JSON.generate('math' => latex_content, 'format' => format, 'ex' => POINTS_PER_EX)
|
|
150
|
+
@stdin.flush
|
|
151
|
+
answer = read_answer
|
|
152
|
+
[answer['svg'], answer['error']]
|
|
135
153
|
rescue Errno::ENOENT => e
|
|
136
|
-
|
|
137
|
-
|
|
154
|
+
[nil, "Node.js executable 'node' was not found. Please install Node.js and ensure 'node' is available on your PATH. Original error: #{e.message}"]
|
|
155
|
+
rescue IOError, SystemCallError => e
|
|
156
|
+
stop
|
|
157
|
+
[nil, "MathJax process for font #{@math_font} failed: #{e.message}"]
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
private
|
|
161
|
+
|
|
162
|
+
def start
|
|
163
|
+
@stdin, @stdout, @wait_thr = Open3.popen2('node', JS_SCRIPT, '--server', @math_font)
|
|
164
|
+
at_exit { stop }
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Skips anything else a dependency may print to stdout.
|
|
168
|
+
def read_answer
|
|
169
|
+
while (line = @stdout.gets)
|
|
170
|
+
answer = JSON.parse(line) rescue nil
|
|
171
|
+
return answer if answer.is_a?(Hash) && (answer.key?('svg') || answer.key?('error'))
|
|
172
|
+
end
|
|
173
|
+
raise IOError, 'render.js ended without an answer'
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def stop
|
|
177
|
+
return unless @stdin
|
|
178
|
+
@stdin.close unless @stdin.closed?
|
|
179
|
+
Process.kill('KILL', @wait_thr.pid) unless @wait_thr.join(5)
|
|
180
|
+
@stdin = @stdout = @wait_thr = nil
|
|
181
|
+
rescue SystemCallError
|
|
182
|
+
@stdin = @stdout = @wait_thr = nil
|
|
138
183
|
end
|
|
139
|
-
[svg_output, error]
|
|
140
184
|
end
|
|
141
185
|
|
|
142
186
|
def adjust_svg_to_match_text(svg_content, font)
|