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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3fc66d0f0c7026c39d6363d44464ad9c5f430aed3202b344de302604679d3166
4
- data.tar.gz: 62009314bb1e5e72e759e2251ce7eaa7ffc14bb123a365a9c0f88dcc58d04bac
3
+ metadata.gz: 3ef66936477b46fb7aceae1c15a22cb42ebcea5d227ab19be302493e55efc9d2
4
+ data.tar.gz: df675d9c39be83650834c20b818c437c75bf842a117be0ba5fa4c4ce0e23eddb
5
5
  SHA512:
6
- metadata.gz: 5234f95491bf7ec6320942e21c009f77575567a4312b1651e13736f19b1cb85d8eeee8cd9b3e443e483628c0b1e7d18847384cb98e9b4cab5efafb539973bcc8
7
- data.tar.gz: 7e89244f2911bc889e4f2a3cdd2d34fc31fde201bacac64a0d995ddcbfbd14afe367e98c20922c828ce80bed93e0e05877cd2dbfb81fd655adda92e5c396a6e0
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 font = server ? process.argv[3] : process.argv[5];
17
+ const fontArg = server ? process.argv[3] : process.argv[5];
16
18
 
17
- mj.config({
18
- MathJax: {
19
- SVG: {
20
- font: font
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
- 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
- });
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
- svg_height_difference = font.font_full_height - svg_height
203
- svg_relative_height_difference = font.font_full_height / svg_height
204
- embedding_text_relative_baseline_height = font.font_baseline / font.font_full_height
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
- if svg_height_difference < 0
208
- svg_relative_portion_extending_embedding_text_below = (1 - svg_relative_height_difference) / 2
209
- svg_relative_baseline_height = embedding_text_relative_baseline_height * svg_relative_height_difference
210
- svg_inner_relative_offset = svg_relative_baseline_height + svg_relative_portion_extending_embedding_text_below - 1
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
- svg_height = font.font_full_height
226
- svg_inner_height = svg_relative_height_difference * svg_inner_height
227
- svg_inner_offset = (embedding_text_relative_baseline_height - 1) * svg_inner_height
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.6.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-node
157
- for AsciiDoctor PDF.
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-Node for full functionality (e.g., LaTeX rendering).
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-node
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