asciidoctor-pdf-mathjax 0.6.0 → 0.7.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 +121 -40
- data/lib/asciidoctor-pdf-mathjax.rb +24 -23
- metadata +6 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3ef66936477b46fb7aceae1c15a22cb42ebcea5d227ab19be302493e55efc9d2
|
|
4
|
+
data.tar.gz: df675d9c39be83650834c20b818c437c75bf842a117be0ba5fa4c4ce0e23eddb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 204c9d8cb2f5fc184ca1e968d4666bcb971fe1e44dba2306d6230a0a2800a22178bfd417fc032a64e9e989d43ac4f89a027de8c99ba3e244c49c683986ad725f
|
|
7
|
+
data.tar.gz: e908b87d74694eddce9063863755e4119bbe9ad6adc7959c8373da173b3ca75bcb6aaf1cecf8a3559448762af696928091df1a53f3e727fae3055888a7b55a8a
|
data/bin/render.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const mj = require('mathjax-node');
|
|
4
|
-
|
|
5
3
|
// Two modes:
|
|
6
4
|
// render.js <latex> <format> <pixels_per_ex> <font>
|
|
7
5
|
// renders one formula, prints the SVG and exits.
|
|
@@ -11,48 +9,131 @@ const mj = require('mathjax-node');
|
|
|
11
9
|
// per formula, {"svg": "..."} or {"error": "..."}.
|
|
12
10
|
// Starting MathJax takes seconds; the server mode pays that once per font
|
|
13
11
|
// instead of once per formula.
|
|
12
|
+
//
|
|
13
|
+
// Font names are MathJax 4 names (mathjax-tex, mathjax-stix2, ...).
|
|
14
|
+
// The MathJax 2 names previously passed to mathjax-node are still accepted.
|
|
15
|
+
|
|
14
16
|
const server = process.argv[2] === '--server';
|
|
15
|
-
const
|
|
17
|
+
const fontArg = server ? process.argv[3] : process.argv[5];
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
const LEGACY_FONTS = {
|
|
20
|
+
'TeX': 'mathjax-tex',
|
|
21
|
+
'STIX-Web': 'mathjax-stix2',
|
|
22
|
+
'Asana-Math': 'mathjax-asana',
|
|
23
|
+
'Neo-Euler': 'mathjax-euler',
|
|
24
|
+
'Gyre-Pagella': 'mathjax-pagella',
|
|
25
|
+
'Gyre-Termes': 'mathjax-termes',
|
|
26
|
+
'Latin-Modern': 'mathjax-modern',
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
function resolveFont(name) {
|
|
30
|
+
const requested = (name && String(name).trim()) || 'TeX';
|
|
31
|
+
const font = LEGACY_FONTS[requested] || requested;
|
|
32
|
+
// Neo Euler ships as a font extension in MathJax 4, not a standalone font.
|
|
33
|
+
if (font === 'mathjax-euler') {
|
|
34
|
+
return { font: 'mathjax-tex', fontExtensions: ['mathjax-euler'] };
|
|
22
35
|
}
|
|
23
|
-
}
|
|
24
|
-
mj.start();
|
|
25
|
-
|
|
26
|
-
async function convertToSvg(latex, format, pixels_per_ex) {
|
|
27
|
-
const data = await mj.typeset({
|
|
28
|
-
ex: pixels_per_ex,
|
|
29
|
-
math: latex,
|
|
30
|
-
format: format,
|
|
31
|
-
svg: true,
|
|
32
|
-
});
|
|
33
|
-
return data.svg;
|
|
36
|
+
return { font: font, fontExtensions: [] };
|
|
34
37
|
}
|
|
35
38
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
39
|
+
function assertInstalled(packageName) {
|
|
40
|
+
try {
|
|
41
|
+
// Font packages export no main entry, so resolve a file they do export.
|
|
42
|
+
require.resolve(packageName + '/package.json');
|
|
43
|
+
} catch (err) {
|
|
44
|
+
throw new Error(
|
|
45
|
+
`Cannot load ${packageName}. Install MathJax 4 with npm, for example: npm install ${packageName}@4`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const { font, fontExtensions } = resolveFont(fontArg);
|
|
51
|
+
|
|
52
|
+
let MathJax;
|
|
53
|
+
try {
|
|
54
|
+
assertInstalled('@mathjax/src');
|
|
55
|
+
assertInstalled(`@mathjax/${font}-font`);
|
|
56
|
+
fontExtensions.forEach((extension) => assertInstalled(`@mathjax/${extension}-font-extension`));
|
|
57
|
+
MathJax = require('@mathjax/src');
|
|
58
|
+
} catch (err) {
|
|
59
|
+
console.error(err.message);
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function convertToSvg(latex, format, pixelsPerEx) {
|
|
64
|
+
const display = format !== 'inline-TeX';
|
|
65
|
+
const ex = Number(pixelsPerEx) || 6;
|
|
66
|
+
return MathJax.tex2svgPromise(latex, {
|
|
67
|
+
display: display,
|
|
68
|
+
em: ex * 2,
|
|
69
|
+
ex: ex,
|
|
70
|
+
// Formulas are embedded at their natural width. A wide container keeps
|
|
71
|
+
// MathJax from inserting line breaks when a width is still consulted.
|
|
72
|
+
containerWidth: 100000,
|
|
73
|
+
}).then((node) => {
|
|
74
|
+
const adaptor = MathJax.startup.adaptor;
|
|
75
|
+
const svg = adaptor.tags(node, 'svg')[0];
|
|
76
|
+
if (!svg) {
|
|
77
|
+
throw new Error('MathJax produced no SVG');
|
|
78
|
+
}
|
|
79
|
+
const xml = adaptor.serializeXML(svg);
|
|
80
|
+
const mathError = xml.match(/data-mjx-error="([^"]*)"/);
|
|
81
|
+
if (mathError) {
|
|
82
|
+
throw new Error(mathError[1]);
|
|
83
|
+
}
|
|
84
|
+
return xml;
|
|
49
85
|
});
|
|
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
86
|
}
|
|
87
|
+
|
|
88
|
+
MathJax.init({
|
|
89
|
+
loader: {
|
|
90
|
+
load: ['input/tex', 'output/svg'],
|
|
91
|
+
// Font packages can report an older component version than MathJax itself.
|
|
92
|
+
versionWarnings: false,
|
|
93
|
+
},
|
|
94
|
+
output: {
|
|
95
|
+
font: font,
|
|
96
|
+
fontExtensions: fontExtensions,
|
|
97
|
+
displayOverflow: 'overflow',
|
|
98
|
+
linebreaks: { inline: false },
|
|
99
|
+
},
|
|
100
|
+
svg: {
|
|
101
|
+
fontCache: 'local',
|
|
102
|
+
},
|
|
103
|
+
startup: {
|
|
104
|
+
typeset: false,
|
|
105
|
+
},
|
|
106
|
+
}).then(() => {
|
|
107
|
+
if (server) {
|
|
108
|
+
let queue = Promise.resolve();
|
|
109
|
+
const input = require('readline').createInterface({ input: process.stdin });
|
|
110
|
+
input.on('line', (line) => {
|
|
111
|
+
queue = queue.then(async () => {
|
|
112
|
+
let answer;
|
|
113
|
+
try {
|
|
114
|
+
const request = JSON.parse(line);
|
|
115
|
+
answer = { svg: await convertToSvg(request.math, request.format, request.ex) };
|
|
116
|
+
} catch (err) {
|
|
117
|
+
answer = { error: String(err && err.message ? err.message : err) };
|
|
118
|
+
}
|
|
119
|
+
process.stdout.write(JSON.stringify(answer) + '\n');
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
input.on('close', () => {
|
|
123
|
+
queue.then(() => {
|
|
124
|
+
if (MathJax.done) MathJax.done();
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
} else {
|
|
128
|
+
const latex = process.argv[2];
|
|
129
|
+
const format = process.argv[3];
|
|
130
|
+
const pixelsPerEx = parseInt(process.argv[4], 10);
|
|
131
|
+
return convertToSvg(latex, format, pixelsPerEx).then((svg) => {
|
|
132
|
+
process.stdout.write(svg + '\n');
|
|
133
|
+
if (MathJax.done) MathJax.done();
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}).catch((err) => {
|
|
137
|
+
console.error(err && err.message ? err.message : err);
|
|
138
|
+
process.exit(1);
|
|
139
|
+
});
|
|
@@ -199,32 +199,33 @@ class AsciidoctorPDFExtensions < (Asciidoctor::Converter.for 'pdf')
|
|
|
199
199
|
svg_width = svg_width * scaling_factor
|
|
200
200
|
svg_height = svg_height * scaling_factor
|
|
201
201
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
202
|
+
# Points per viewBox unit. Held constant so the glyphs keep the size MathJax produced.
|
|
203
|
+
pt_per_unit = svg_height / svg_inner_height
|
|
204
|
+
orig_top = svg_inner_offset
|
|
205
|
+
orig_bottom = svg_inner_offset + svg_inner_height
|
|
206
|
+
|
|
207
|
+
# Line-sized window with the math baseline (y = 0) on the text baseline.
|
|
208
|
+
# Asciidoctor centers an inline image in the line when it is at most 1.5 line
|
|
209
|
+
# heights tall, and bottom-aligns it otherwise. A window exactly the line height
|
|
210
|
+
# crops any ink past the font ascender or descender (subscripts, denominators).
|
|
211
|
+
line_units = font.font_full_height / pt_per_unit
|
|
212
|
+
line_bottom = font.font_baseline / pt_per_unit
|
|
213
|
+
line_top = line_bottom - line_units
|
|
214
|
+
overflow_above = [line_top - orig_top, 0].max
|
|
215
|
+
overflow_below = [orig_bottom - line_bottom, 0].max
|
|
216
|
+
# Pad both sides equally so centering still leaves the baseline on the text baseline.
|
|
217
|
+
extra = [overflow_above, overflow_below].max
|
|
205
218
|
|
|
206
219
|
logger.debug "Original SVG height: #{svg_height.round(2)}, width: #{svg_width.round(2)}, inner height: #{svg_inner_height.round(2)}, inner offset: #{svg_inner_offset.round(2)}"
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
svg_inner_offset_new = svg_inner_relative_offset * svg_inner_height
|
|
213
|
-
svg_inner_height_padding = (svg_inner_offset - svg_inner_offset_new) * 0.25 # 25% padding to handle fractions
|
|
214
|
-
svg_inner_height_difference = 2 * svg_inner_height_padding
|
|
215
|
-
svg_inner_height_new = svg_inner_height + svg_inner_height_difference
|
|
216
|
-
svg_inner_height_relative_difference = svg_inner_height_new / svg_inner_height
|
|
217
|
-
|
|
218
|
-
logger.debug("svg_inner_offset = #{svg_inner_offset}, svg_inner_height = #{svg_inner_height}, svg_inner_offset_new = #{svg_inner_offset_new}, svg_inner_height_new = #{svg_inner_height_new}")
|
|
219
|
-
logger.debug("svg_inner_offset_diff = #{svg_inner_offset - svg_inner_offset_new}, svg_inner_offset_diff_relative = #{(svg_inner_offset - svg_inner_offset_new) / svg_inner_height}")
|
|
220
|
-
|
|
221
|
-
svg_height = svg_height * svg_inner_height_relative_difference
|
|
222
|
-
svg_inner_height = svg_inner_height_new
|
|
223
|
-
svg_inner_offset = svg_inner_offset_new - svg_inner_height_padding
|
|
220
|
+
fitted_height = (line_units + 2 * extra) * pt_per_unit
|
|
221
|
+
if fitted_height <= font.font_full_height * 1.5
|
|
222
|
+
svg_inner_offset = line_top - extra
|
|
223
|
+
svg_inner_height = line_units + 2 * extra
|
|
224
|
+
svg_height = fitted_height
|
|
224
225
|
else
|
|
225
|
-
|
|
226
|
-
svg_inner_height =
|
|
227
|
-
|
|
226
|
+
svg_inner_offset = orig_top
|
|
227
|
+
svg_inner_height = orig_bottom - orig_top
|
|
228
|
+
svg_height = svg_inner_height * pt_per_unit
|
|
228
229
|
end
|
|
229
230
|
|
|
230
231
|
view_box[1] = svg_inner_offset
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: asciidoctor-pdf-mathjax
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Crown0815
|
|
@@ -153,8 +153,8 @@ dependencies:
|
|
|
153
153
|
- - "~>"
|
|
154
154
|
- !ruby/object:Gem::Version
|
|
155
155
|
version: '1.4'
|
|
156
|
-
description: Converts STEM blocks and inline macros to SVG images using MathJax
|
|
157
|
-
|
|
156
|
+
description: Converts STEM blocks and inline macros to SVG images using MathJax for
|
|
157
|
+
AsciiDoctor PDF.
|
|
158
158
|
executables:
|
|
159
159
|
- render.js
|
|
160
160
|
extensions: []
|
|
@@ -168,10 +168,10 @@ licenses:
|
|
|
168
168
|
metadata: {}
|
|
169
169
|
post_install_message: |
|
|
170
170
|
Thank you for installing asciidoctor-pdf-mathjax!
|
|
171
|
-
Note: This gem requires MathJax
|
|
171
|
+
Note: This gem requires MathJax 4 for full functionality (e.g., LaTeX rendering).
|
|
172
172
|
If you haven't installed it, run:
|
|
173
|
-
npm install -g mathjax-
|
|
174
|
-
See the README for details.
|
|
173
|
+
npm install -g @mathjax/src@4 @mathjax/mathjax-tex-font@4
|
|
174
|
+
See the README for details and for the other math fonts.
|
|
175
175
|
rdoc_options: []
|
|
176
176
|
require_paths:
|
|
177
177
|
- lib
|