Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 7 additions & 9 deletions src/imageGenerator.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,6 @@ const { log } = require('console');
const adaptor = liteAdaptor();
RegisterHTMLHandler(adaptor);

const extraPackages = ['physics'];

// Configure TeX input
const tex = new TeX({
packages: AllPackages.concat(extraPackages),
inlineMath: [['$', '$'], ['\\(', '\\)']],
displayMath: [['$$', '$$'], ['\\[', '\\]']]
});

const mathml = new MathML();
const asciimath = new AsciiMath();

Expand All @@ -45,6 +36,13 @@ module.exports.generate = async (configs, req, res, next) => {
let dpi = query.dpi;
let isSvg = query.svg === true || query.svg === '1' || query.svg === 'true';

// Configure TeX input
let tex = new TeX({
packages: configs.typeset.math.includes('\\require{physics}') ? AllPackages.concat(['physics']) : AllPackages,
inlineMath: [['$', '$'], ['\\(', '\\)']],
displayMath: [['$$', '$$'], ['\\[', '\\]']]
});

let inputFormat = tex;

switch (configs.typeset.format) {
Expand Down
96 changes: 56 additions & 40 deletions test/latex.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ describe('Testing the /latex route', function() {
// ✅ LaTeX Delimiters
it('Should render inline LaTeX delimiters \( ... \)', function() {
return request(app)
.get('/latex?latex=%5C(%20alpha%20A%20%5Cchi%20X%20%5C)&fg=000000&svg=1')
.get('/latex?latex=XCggXFxhbHBoYSBBIFxcY2hpIFggXCk&isBase64=1&svg=1')
.then(function(response) {
expect(response.type).to.contain('image/svg+xml');
});
});

it('Should render block LaTeX delimiters \[ ... \]', function() {
return request(app)
.get('/latex?latex=%5C[%20sum_%7Bi%3D0%7D%5En%20i%5E2%20%3D%20%5Cfrac%7B(n%5E2%2Bn)(2n%2B1)%7D%7B6%7D%20%5C]&fg=000000&svg=1')
.get('/latex?latex=XFtcIFxcc3VtX3tpPTB9Xm4gaV4yID0gXFxmcmFjeyhuXjIrbil7Mn0oMm4rMSl9ezZ9IFxd&isBase64=1&svg=1')
.then(function(response) {
expect(response.type).to.contain('image/svg+xml');
});
Expand All @@ -32,15 +32,15 @@ describe('Testing the /latex route', function() {
// ✅ Dollar Sign Delimiters
it('Should render inline math using dollar signs', function() {
return request(app)
.get('/latex?latex=%24alpha%20A%20%5Cchi%20X%20%24&fg=000000&svg=1')
.get('/latex?latex=JFxcYWxwaGEgQSBcXGNoaSBYICQ&isBase64=1&svg=1')
.then(function(response) {
expect(response.type).to.contain('image/svg+xml');
});
});

it('Should render block math using double dollar signs', function() {
return request(app)
.get('/latex?latex=%24%24sum_%7Bi%3D0%7D%5En%20i%5E2%20%3D%20%5Cfrac%7B(n%5E2%2Bn)(2n%2B1)%7D%7B6%7D%24%24&fg=000000&svg=1')
.get('/latex?latex=JCRcXHN1bV97aT0wfV5uIGleMiA9IFxcZnJhY3sobl4yK24pKDJuKzEpfXs2fSQk&isBase64=1&svg=1')
.then(function(response) {
expect(response.type).to.contain('image/svg+xml');
});
Expand All @@ -49,7 +49,7 @@ describe('Testing the /latex route', function() {
// ✅ AMS Math Package
it('Should render AMS align environment', function() {
return request(app)
.get('/latex?latex=%5C[%5Cbegin%7Balign%7D%20E%20%26%3D%20mc%5E2%20%5C%5C%20F%20%26%3D%20ma%20%5Cend%7Balign%7D%5C]&fg=000000&svg=1')
.get('/latex?latex=XFtcYmVnaW57YWxpZ259IEUgJj0gbWNeMiBcXFxcIEYgJj0gbWEgXGVuZHthbGlnbn1cXQ&isBase64=1&svg=1')
.then(function(response) {
expect(response.type).to.contain('image/svg+xml');
});
Expand All @@ -58,7 +58,7 @@ describe('Testing the /latex route', function() {
// ✅ Mathtools
it('Should render Mathtools gather environment', function() {
return request(app)
.get('/latex?latex=%5C[%5Cbegin%7Bgathered%7D%20a%20%5Ccoloneqq%20b%20%2B%20c%20%5C%5C%20x%20%5Ciff%20y%20%5Cend%7Bgathered%7D%5C]&fg=000000&svg=1')
.get('/latex?latex=XFtcYmVnaW57Z2F0aGVyZWR9IGEgXGNvbG9uZXFxIGIgKyBjIFxcXFwgeCBcaWZmIHkgXGVuZHtnYXRoZXJlZH1cXQ&isBase64=1&svg=1')
.then(function(response) {
expect(response.type).to.contain('image/svg+xml');
});
Expand All @@ -67,7 +67,7 @@ describe('Testing the /latex route', function() {
// ✅ Physics Package
it('Should render physics macros', function() {
return request(app)
.get('/latex?latex=%5C[%20%5Cdv%7Bf%7D%7Bx%7D%2C%20%5Cpdv%7Bf%7D%7Bx%7D%2C%20%5Cexpval%7BA%7D%2C%20%5Cket%7B%5Cpsi%7D%20%5C]&fg=000000&svg=1')
.get('/latex?latex=XFtcIFxcZHZ7Zn17eH0sIFxccGR2e2Z9e3h9LCBcXGV4cHZhbHtBfSwgXFxrZXR7XFxwcml9IFxd&isBase64=1&svg=1')
.then(function(response) {
expect(response.type).to.contain('image/svg+xml');
});
Expand All @@ -76,7 +76,7 @@ describe('Testing the /latex route', function() {
// ✅ Braket Package
it('Should render Dirac bra-ket notation', function() {
return request(app)
.get('/latex?latex=%5C[%20%5Cbra%7B%5Cpsi%7D%20H%20%5Cket%7B%5Cphi%7D%20%3D%20E%20%5Cbraket%7B%5Cpsi%20%7C%20%5Cphi%7D%20%5C]&fg=000000&svg=1')
.get('/latex?latex=XFtcIFxcYnJhe1xccHNpfSBIIFxca2V0e1xccGhpfSA9IEUgXFxicmFrZXR7XFxwc2kgfCBcXHBoaX0gXF0&isBase64=1&svg=1')
.then(function(response) {
expect(response.type).to.contain('image/svg+xml');
});
Expand All @@ -85,7 +85,7 @@ describe('Testing the /latex route', function() {
// ✅ mhchem
it('Should render chemical equations', function() {
return request(app)
.get('/latex?latex=%5C[%20%5Cce%7B2H2%20%2B%20O2%20-%3E%202H2O%7D%20%5C]&fg=000000&svg=1')
.get('/latex?latex=XFtcIFxcY2V7MkgyICsgTzIgLT4gMkgyT30gXF0&isBase64=1&svg=1')
.then(function(response) {
expect(response.type).to.contain('image/svg+xml');
});
Expand All @@ -94,7 +94,7 @@ describe('Testing the /latex route', function() {
// ✅ Text Macros
it('Should render text macros', function() {
return request(app)
.get('/latex?latex=%5C[%20%5Ctextbf%7BBold%7D%20%5Ctextit%7BItalic%7D%20%5Ctextsc%7BSmallCaps%7D%20%5Ctexttt%7BMonospace%7D%20%5C]&fg=000000&svg=1')
.get('/latex?latex=XFtcIFxcYmVmdGV4dHtCb2xkfSBcXGl0YWxpY3tJdGFsaWN9IFxcdGV4dHNje1NtYWxsQ2Fwc30gXFx0ZXh0dHR7TW9ub3NwYWNlfSBcXQ&isBase64=1&svg=1')
.then(function(response) {
expect(response.type).to.contain('image/svg+xml');
});
Expand All @@ -103,7 +103,7 @@ describe('Testing the /latex route', function() {
// ✅ Color Package
it('Should render colored text', function() {
return request(app)
.get('/latex?latex=%5C[%20%5Ctextcolor%7Bred%7D%7BThis%20is%20red%20text%7D%20%5Cquad%20%5Ccolor%7Bblue%7DThis%20is%20blue%20text%20%5C]&fg=000000&svg=1')
.get('/latex?latex=XFtcIFxcdGV4dGNvbG9ye3JlZH17VGhpcyBpcyByZWQgdGV4dH0gXFxxdWFkIFxcY29sb3J7Ymx1ZX1UaGlzIGlzIGJsdWUgdGV4dCBcXQ&isBase64=1&svg=1')
.then(function(response) {
expect(response.type).to.contain('image/svg+xml');
});
Expand All @@ -112,7 +112,7 @@ describe('Testing the /latex route', function() {
// ✅ Enclose Package
it('Should render enclosed symbols', function() {
return request(app)
.get('/latex?latex=%5C[%20%5Cenclose%7Bcircle%7D%7Bx%7D%2C%20%5Cenclose%7Bbox%7D%7By%7D%2C%20%5Cenclose%7Bupdiagonalstrike%7D%7Bz%7D%20%5C]&fg=000000&svg=1')
.get('/latex?latex=XFtcIFxcZW5jbG9zZXtjaXJjbGV9e3h9LCBcXGVuY2xvc2V7Ym94fXt5fSwgXFxlbmNsb3Nle3VwZGlhZ29uYWxzdHJpa2V9e3p9IFxd&isBase64=1&svg=1')
.then(function(response) {
expect(response.type).to.contain('image/svg+xml');
});
Expand All @@ -121,7 +121,7 @@ describe('Testing the /latex route', function() {
// ✅ Tag Formatting
it('Should render equation with a custom tag', function() {
return request(app)
.get('/latex?latex=%5C[%20%5Cbegin%7Bequation%7D%20E%20%3D%20mc%5E2%20%5Ctag%7BEinstein%7D%20%5Cend%7Bequation%7D%20%5C]&fg=000000&svg=1')
.get('/latex?latex=XFtcIFxcYmVnaW57ZXF1YXRpb259IEUgPSBtY14yIFxcdGFne0VpbnN0ZWlufSBcXGVuZHtlcXVhdGlvbn0gXF0&isBase64=1&svg=1')
.then(function(response) {
expect(response.type).to.contain('image/svg+xml');
});
Expand All @@ -130,47 +130,63 @@ describe('Testing the /latex route', function() {
// ✅ Matrix Formatting
it('Should render a matrix', function() {
return request(app)
.get('/latex?latex=\\det \\begin{bmatrix} a %26 b \\\\ c %26 d \\end{bmatrix} %3D ad %26%238211%3B bc&fg=000000&svg=1')
.get('/latex?latex=XFtcZGV0IFxiZWdpbntibWF0cml4fSBhICYgYiBcXFxcIGMgJiBkIFxlbmR7Ym1hdHJpeH0gPSBhZCAtIGJjXF0&isBase64=1&svg=1')
.then(function(response) {
expect(response.type).to.contain('image/svg+xml');
});
});

// ✅ Require Package not required because all commands are included in the default MathJax configuration
it('Should strip require command and return parsed formula', function() {
it('Should load physics package for base64 encoded formula when required', function() {
// Base64 encoded: \require{physics} \divergence \vec{E} = \frac{\rho}{\epsilon_0}
return request(app)
.get('/latex?latex=\\require{physics} \\( \\dv{f}{x}, \\pdv{f}{x}, \\expval{A}, \\ket{\\psi} \\)&svg=1')
.get('/latex?latex=XHJlcXVpcmV7cGh5c2ljc30gXGRpdmVyZ2VuY2UgXHZlY3tFfSA9IFxmcmFjeyRccmhvfXskXGVwc2lsb25fMH0=&svg=1&isBase64=1')
.then(function(response) {
expect(response.type).to.contain('image/svg+xml');
const svgContent = response.body.toString('utf-8');
expect(svgContent).to.include('svg');

// Extract width from SVG
const widthMatch = svgContent.match(/width="([0-9.]+)([a-zA-Z]+)"/);
expect(widthMatch).to.not.be.null;
const width = parseFloat(widthMatch[1]);
const unit = widthMatch[2];

// Convert to ex if needed
let widthInEx = width;
if (unit === 'px') {
widthInEx = width / 8; // Approximate conversion from px to ex
}

// Width should be less than 30ex (indicating proper symbol rendering)
expect(widthInEx).to.be.lessThan(30);
});
});

});

it('Should return type of image as png', function() {
return request(app)
.get('/latex?latex=eF5uICsgeV5uID0gel5u&isBase64=1&svg=0')
.then(function(response) {
expect(response.type).to.contain('image/png');
});
});

it('Should return type of image as png', function() {
return request(app).
get('/latex?latex=x%5En%20%2B%20y%5En%20%3D%20z%5En&fg=000000&svg=0').
then(function(response) {
expect(response.type).to.contain('image/png');
});
});
it('Should return type of image as png by default', function() {
return request(app)
.get('/latex?latex=eF5uICsgeV5uID0gel5u&isBase64=1')
.then(function(response) {
expect(response.type).to.contain('image/png');
});
});

it('Should return type of image as png', function() {
return request(app).
get('/latex?latex=x%5En%20%2B%20y%5En%20%3D%20z%5En&fg=000000').
then(function(response) {
expect(response.type).to.contain('image/png');
});
});

it('Bad LaTex should return error status', function() {
return request(app).
get('/latex?latex=%5CLaTeX%26s%3DX&svg=1').
then(function(response) {
expect(response.type).to.contain('image/png');
expect(response.get('pb-mathjax-error')).to.equal('Formula does not parse');
});
});
it('Bad LaTex should return error status', function() {
return request(app)
.get('/latex?latex=XFxMYVRlWCZzPVg&isBase64=1&svg=1')
.then(function(response) {
expect(response.type).to.contain('image/png');
expect(response.get('pb-mathjax-error')).to.equal('Formula does not parse');
});
});

});