This repository has been archived by the owner on Apr 1, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.js
204 lines (181 loc) · 6 KB
/
main.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
/*
* main.js
*
* Copyright (c) 2019 American Mathematical Society
*
*/
process.on('unhandledRejection', r => console.log(r));
const fs = require('fs');
const crypto = require('crypto');
const compareImages = require('resemblejs/compareImages');
//
// MathJax v2 setup
//
// promise around mathjax-node-sre
const mj2 = require('mathjax-node-sre');
const mj2promise = async data =>
new Promise(function(resolve, reject) {
mj2.typeset(data, function(data) {
// HACK we want a promise but handle errors ourselves
if (false) reject(data.errors);
else resolve(data);
});
});
// TODO add extensions
mj2.config({
MathJax: {
displayAlign: 'left',
TeX: {
TagSide: 'left'
},
SVG: {
// font: 'STIX-Web',
blacker: 0
}
}
});
mj2.start();
const mjInputDefault = {
svg: true,
mml: true,
useGlobalCache: false,
width: 0,
ex: 7.52, // ex height to match Times
speakText: true
};
//
// svg to png
//
const puppeteer = require('puppeteer');
const svg2png = async svgstring => {
const browser = await puppeteer.launch();
process.on('SIGINT', async () => {
await browser.close();
process.exit(1);
});
const page = await browser.newPage();
await page.setViewport({ width: 1000, height: 1000, deviceScaleFactor: 4 });
await page.goto(
'data:text/html, %3C!DOCTYPE%20html%3E%3Chtml%3E%3Chead%3E%3Cmeta%20charset%3D%22utf-8%22%3E%3Cmeta%20name%3D%22viewport%22%20content%3D%22width%3Ddevice-width%22%3E%3Ctitle%3Etitle%3C%2Ftitle%3E%3C%2Fhead%3E%3Cbody%3E%3C%2Fbody%3E%3C%2Fhtml%3E'
);
await page.evaluate(
s =>
(document.body.innerHTML =
'<span style="display:inline-flex; padding: 5px; height: 500px; width: 500px;">' + //padding b/c v3 may be cut off
s +
'</span>'),
svgstring.replace(/width="(.*?)"/s,'width="100%"').replace(/height=".*?"/s,'')
);
const svg = await page.$('span');
const result = await svg.screenshot();
await browser.close();
return result;
};
//
// MathJax v3 setup
//
const TeX = require('mathjax3/mathjax3/input/tex.js').TeX;
const SVG = require('mathjax3/mathjax3/output/svg.js').SVG;
const HTMLDocument = require('mathjax3/mathjax3/handlers/html/HTMLDocument.js')
.HTMLDocument;
const liteAdaptor = require('mathjax3/mathjax3/adaptors/liteAdaptor.js')
.liteAdaptor;
const AllPackages = require('mathjax3/mathjax3/input/tex/AllPackages.js')
.AllPackages;
const tex = new TeX({ packages: AllPackages, TagSide: 'left' });
const svg = new SVG({ exFactor: 0.47 });
const adaptor = liteAdaptor();
const html = new HTMLDocument('', adaptor, { InputJax: tex, OutputJax: svg });
// patches
// cf. https://github.com/mathjax/mathjax-v3/issues/184
top = true;
// cf https://github.com/mathjax/mathjax-v3/issues/186
const MmlMath = require('mathjax3/mathjax3/core/MmlTree/MmlNodes/math.js')
.MmlMath;
MmlMath.defaults.indentalign = 'left';
const mj3 = (string, display, em = 16, ex = 7.52, cwidth = 0) => {
const math = new html.options.MathItem(string, tex, display);
math.setMetrics(em, ex, cwidth, 100000, 1);
math.compile(html);
math.typeset(html);
return adaptor.innerHTML(math.typesetRoot);
};
const diff = async (texstring, format) => {
const texstringhash = crypto
.createHash('md5')
.update(texstring)
.digest('hex');
let svg2 = {};
let svg2sre = {};
let svg3 = {};
// run mj2
const mj2Input = Object.assign({}, mjInputDefault);
mj2Input.math = texstring;
mj2Input.format = format;
svg2 = await mj2promise(mj2Input);
if (svg2.errors) return new Error('mathjax error');
// fs.writeFileSync(texstringhash + '-v2.svg', svg2.svg);
// run mj2 + SRE enrichment
const mj2sreInput = Object.assign({}, mjInputDefault);
mj2sreInput.math = texstring;
mj2sreInput.enrich = true;
mj2sreInput.format = 'TeX';
svg2sre = await mj2promise(mj2sreInput);
// fs.writeFileSync(texstringhash + '-SRE.svg', svg2sre.svg);
// run mj3
const isDisplay = format === 'TeX';
try {
svg3.svg = await mj3(texstring, isDisplay);
} catch (e) {
console.log('mj3 error');
return;
}
// fs.writeFileSync(texstringhash + '-v3.svg', svg3.svg);
// transfer viewBox, styles
const viewboxregex = /viewBox=".*?"/s;
const viewboxv3 = svg3.svg.match(viewboxregex)[0];
const styleregex = /style=".*?"/s;
const stylev3 = svg3.svg.match(styleregex)[0];
// svg2png
const png2 = await svg2png(svg2.svg.replace(viewboxregex,viewboxv3).replace(styleregex,stylev3));
const pngsre = await svg2png(svg2sre.svg.replace(viewboxregex,viewboxv3).replace(styleregex,stylev3));
const png3 = await svg2png(svg3.svg);
// diff
const options = {
scaleToSameSize: true,
ignore: 'antialiasing'
};
const diff_2_sre = await compareImages(png2, pngsre, options);
console.log('v2 vs SRE - Same dimension? ' + diff_2_sre.isSameDimensions);
console.log('v2 vs SRE - Mismatch: ' + diff_2_sre.misMatchPercentage);
const diff_2_3 = await compareImages(png2, png3, options);
console.log('v2 vs v3 - Same dimension? ' + diff_2_3.isSameDimensions);
console.log('v2 vs v3 - Mismatch: ' + diff_2_3.misMatchPercentage);
// write output if mismatch
const threshold = 0.1;
if (diff_2_sre.misMatchPercentage > threshold || diff_2_3.misMatchPercentage > threshold)
fs.writeFileSync(texstringhash + '-v2.png', png2);
if (diff_2_sre.misMatchPercentage > threshold) {
fs.writeFileSync(texstringhash + '-v2sre.png', pngsre);
fs.writeFileSync(texstringhash + '-v2-v2sre.png', diff_2_sre.getBuffer());
}
if (diff_2_3.misMatchPercentage > threshold) {
fs.writeFileSync(texstringhash + '-v3.png', png3);
fs.writeFileSync(texstringhash + '-v3-v2.png', diff_2_3.getBuffer());
}
};
const main = async input => {
if (!fs.existsSync(input)) {
await diff(input, 'TeX');
return;
}
const eqnStore = JSON.parse(fs.readFileSync(input).toString());
for (let key in eqnStore) {
const entry = eqnStore[key];
console.log(entry['tex'], entry['format']);
await diff(entry['tex'], entry['format']);
}
};
// process CLI arguments
const input = process.argv[2];
main(input);