mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Upgrade MathJax to version 3
version 3 is more performant, smaller and simpler to integrate, without monkeypatching. On the down side, it does not do line breaking, as yet. Note that only the viewer is currently ported
This commit is contained in:
parent
acba68ead4
commit
e2243bf7a9
@ -8,7 +8,7 @@
|
||||
<!-- This script tag is needed to make calibre's ebook-viewer recpgnize that this file needs math typesetting -->
|
||||
<script type="text/x-mathjax-config">
|
||||
// This line adds numbers to all equations automatically, unless explicitly suppressed.
|
||||
MathJax.Hub.Config({ TeX: { equationNumbers: {autoNumber: "all"} } });
|
||||
MathJax.tex = {tags: 'all'};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
Binary file not shown.
@ -7,7 +7,6 @@ __copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import os, json
|
||||
from io import BytesIO
|
||||
from hashlib import sha1
|
||||
|
||||
|
||||
@ -19,9 +18,8 @@ class MathJax(ReVendor):
|
||||
description = 'Create the MathJax bundle'
|
||||
NAME = 'mathjax'
|
||||
TAR_NAME = 'MathJax'
|
||||
VERSION = '2.7.6'
|
||||
VERSION = '3.0.5'
|
||||
DOWNLOAD_URL = 'https://github.com/mathjax/MathJax/archive/%s.tar.gz' % VERSION
|
||||
FONT_FAMILY = 'TeX'
|
||||
|
||||
def add_file_pre(self, name, raw):
|
||||
self.h.update(raw)
|
||||
@ -46,14 +44,10 @@ class MathJax(ReVendor):
|
||||
with self.temp_dir(suffix='-calibre-mathjax-build') as tdir:
|
||||
src = opts.path_to_mathjax or self.download_vendor_release(tdir, opts.mathjax_url)
|
||||
self.info('Adding MathJax...')
|
||||
unpacked = 'unpacked' if self.e(self.j(src, 'unpacked')) else ''
|
||||
self.add_file(self.j(src, unpacked, 'MathJax.js'), 'MathJax.js')
|
||||
for x in 'core loader startup input/tex-full input/asciimath input/mml input/mml/entities output/chtml'.split():
|
||||
self.add_file(self.j(src, 'es5', x + '.js'), x + '.js')
|
||||
self.add_tree(
|
||||
self.j(src, 'fonts', 'HTML-CSS', self.FONT_FAMILY, 'woff'),
|
||||
'fonts/HTML-CSS/%s/woff' % self.FONT_FAMILY,
|
||||
lambda x: not x.endswith('.woff'))
|
||||
for d in 'extensions jax/element jax/input jax/output/CommonHTML'.split():
|
||||
self.add_tree(self.j(src, unpacked, *d.split('/')), d)
|
||||
self.j(src, 'es5', 'output', 'chtml'), 'output/chtml')
|
||||
etag = self.h.hexdigest()
|
||||
with open(self.j(self.RESOURCES, 'mathjax', 'manifest.json'), 'wb') as f:
|
||||
f.write(json.dumps({'etag': etag, 'files': self.mathjax_files, 'version': self.VERSION}, indent=2).encode('utf-8'))
|
||||
|
@ -1,72 +0,0 @@
|
||||
#!/usr/bin/env python2
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
PATCHED_MATHJAX = '''
|
||||
|
||||
function postprocess_mathjax(link_uid) {
|
||||
Array.prototype.forEach.call(document.getElementsByTagName('a'), function(a) {
|
||||
var href = a.getAttribute('href');
|
||||
if (href && href.startsWith('#')) {
|
||||
a.setAttribute('href', 'javascript: void(0)')
|
||||
a.setAttribute('data-' + link_uid, JSON.stringify({'frag':href.slice(1)}))
|
||||
}
|
||||
});
|
||||
document.documentElement.dispatchEvent(new CustomEvent("calibre-mathjax-init-done"));
|
||||
}
|
||||
|
||||
function monkeypatch(mathjax_files) {
|
||||
var orig = window.MathJax.Ajax.fileURL.bind(window.MathJax.Ajax);
|
||||
var StyleString = window.MathJax.Ajax.StyleString.bind(window.MathJax.Ajax);
|
||||
|
||||
window.MathJax.Ajax.StyleString = function(styles) {
|
||||
return StyleString(styles).replace(/url\\('?(.*?)'?\\)/g, function(match, url) {
|
||||
if (!url.endsWith('.woff')) return match;
|
||||
url = mathjax_files[url];
|
||||
if (!url) return match;
|
||||
if (typeof url != "string") {
|
||||
url = window.URL.createObjectURL(url);
|
||||
mathjax_files[name] = url;
|
||||
}
|
||||
return "url('" + url + "')";
|
||||
});
|
||||
}
|
||||
|
||||
window.MathJax.Ajax.fileURL = function(mathjax_name) {
|
||||
var ans = orig(mathjax_name);
|
||||
var name = ans.replace(/^\\//g, '');
|
||||
if (name.startsWith('../fonts')) name = name.slice(3);
|
||||
ans = mathjax_files[name];
|
||||
if (!ans) ans = name;
|
||||
if (typeof ans !== 'string') {
|
||||
mathjax_files[name] = window.URL.createObjectURL(ans);
|
||||
ans = mathjax_files[name];
|
||||
}
|
||||
if (ans === name && !name.startsWith('blob:') && !name.endsWith('/eot') && !name.endsWith('/woff') && !name.endsWith('/otf')) {
|
||||
if (ans.endsWith('.eot') || ans.endsWith('.otf')) ans = '';
|
||||
else console.log('WARNING: Failed to resolve MathJax file: ' + mathjax_name);
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
window.MathJax.Ajax.fileRev = function(mathjax_name) { return ''; }
|
||||
}
|
||||
|
||||
function init_mathjax(link_uid, mathjax_files) {
|
||||
monkeypatch(mathjax_files);
|
||||
window.MathJax.Hub.Register.StartupHook("End", postprocess_mathjax.bind(this, link_uid))
|
||||
}
|
||||
|
||||
document.documentElement.addEventListener("calibre-mathjax-init", function(ev) {
|
||||
|
||||
window.MathJax = ev.detail.MathJax;
|
||||
window.MathJax.AuthorInit = init_mathjax.bind(this, ev.detail.link_uid, ev.detail.mathjax_files);
|
||||
|
||||
__SRC__
|
||||
});
|
||||
'''
|
||||
|
||||
|
||||
def monkeypatch_mathjax(src):
|
||||
return PATCHED_MATHJAX.replace('__SRC__', src, 1)
|
@ -121,7 +121,6 @@ class UrlSchemeHandler(QWebEngineUrlSchemeHandler):
|
||||
def __init__(self, parent=None):
|
||||
QWebEngineUrlSchemeHandler.__init__(self, parent)
|
||||
self.mathjax_dir = P('mathjax', allow_user_override=False)
|
||||
self.mathjax_manifest = None
|
||||
self.allowed_hosts = (FAKE_HOST, SANDBOX_HOST)
|
||||
|
||||
def requestStarted(self, rq):
|
||||
@ -132,7 +131,7 @@ class UrlSchemeHandler(QWebEngineUrlSchemeHandler):
|
||||
if host not in self.allowed_hosts or url.scheme() != FAKE_PROTOCOL:
|
||||
return self.fail_request(rq)
|
||||
name = url.path()[1:]
|
||||
if host == SANDBOX_HOST and not name.startswith('book/'):
|
||||
if host == SANDBOX_HOST and name.partition('/')[0] not in ('book', 'mathjax'):
|
||||
return self.fail_request(rq)
|
||||
if name.startswith('book/'):
|
||||
name = name.partition('/')[2]
|
||||
@ -169,14 +168,6 @@ class UrlSchemeHandler(QWebEngineUrlSchemeHandler):
|
||||
else:
|
||||
rq.fail(rq.UrlNotFound)
|
||||
elif name.startswith('mathjax/'):
|
||||
from calibre.gui2.viewer.mathjax import monkeypatch_mathjax
|
||||
if name == 'mathjax/manifest.json':
|
||||
if self.mathjax_manifest is None:
|
||||
import json
|
||||
from calibre.srv.books import get_mathjax_manifest
|
||||
self.mathjax_manifest = as_bytes(json.dumps(get_mathjax_manifest()['files']))
|
||||
send_reply(rq, 'application/json', self.mathjax_manifest)
|
||||
return
|
||||
path = os.path.abspath(os.path.join(self.mathjax_dir, '..', name))
|
||||
if path.startswith(self.mathjax_dir):
|
||||
mt = guess_type(name)
|
||||
@ -186,10 +177,35 @@ class UrlSchemeHandler(QWebEngineUrlSchemeHandler):
|
||||
except EnvironmentError as err:
|
||||
prints("Failed to get mathjax file: {} with error: {}".format(name, err))
|
||||
return self.fail_request(rq, rq.RequestFailed)
|
||||
if 'MathJax.js' in name:
|
||||
# raw = open(os.path.expanduser('~/work/mathjax/unpacked/MathJax.js')).read()
|
||||
raw = monkeypatch_mathjax(raw.decode('utf-8')).encode('utf-8')
|
||||
|
||||
if name.endswith('/startup.js'):
|
||||
raw = b'''
|
||||
window.MathJax = {};
|
||||
window.MathJax.options = {
|
||||
renderActions: {
|
||||
// disable the mathjax context menu
|
||||
addMenu: [0, '', ''],
|
||||
},
|
||||
};
|
||||
window.MathJax.loader = {
|
||||
load: ['input/tex-full', 'input/asciimath', 'input/mml', 'output/chtml'],
|
||||
};
|
||||
window.MathJax.startup = {
|
||||
ready: () => {
|
||||
MathJax.startup.defaultReady();
|
||||
MathJax.startup.promise.then(() => {
|
||||
document.documentElement.dispatchEvent(new CustomEvent("calibre-mathjax-typeset-done"));
|
||||
});
|
||||
},
|
||||
};
|
||||
for (const s of document.scripts) {
|
||||
if (s.type === "text/x-mathjax-config") {
|
||||
es = document.createElement('script');
|
||||
es.text = s.text;
|
||||
document.head.appendChild(es);
|
||||
document.head.removeChild(es);
|
||||
}
|
||||
}
|
||||
''' + raw
|
||||
send_reply(rq, mt, raw)
|
||||
elif not name:
|
||||
send_reply(rq, 'text/html', viewer_html())
|
||||
|
@ -16,75 +16,20 @@ def get_url(mathjax_files, name):
|
||||
return ans
|
||||
|
||||
|
||||
def monkeypatch(mathjax_files):
|
||||
StyleString = window.MathJax.Ajax.StyleString.bind(window.MathJax.Ajax)
|
||||
|
||||
def style_string(styles):
|
||||
# replace the URLs in the generated stylesheet
|
||||
return StyleString(styles).replace(/url\('?(.*?)'?\)/g, def (match, url):
|
||||
if not url.endsWith('.woff'):
|
||||
return match
|
||||
url = get_url(mathjax_files, url)
|
||||
ans = f"url('{url}')"
|
||||
return ans
|
||||
)
|
||||
|
||||
orig = window.MathJax.Ajax.fileURL.bind(window.MathJax.Ajax)
|
||||
def file_url(file):
|
||||
ans = orig(file)
|
||||
name = ans.lstrip('/')
|
||||
if name.startswith('../fonts'):
|
||||
name = name[3:]
|
||||
if name.rpartition('/')[-1] in 'otf eot woff':
|
||||
return name
|
||||
ans = get_url(mathjax_files, name)
|
||||
if ans is name and not name.startswith('blob:'):
|
||||
if ans.endswith('.eot') or ans.endswith('.otf'):
|
||||
return ''
|
||||
print('WARNING: Failed to resolve MathJax file:', name)
|
||||
return ans
|
||||
window.MathJax.Ajax.fileURL = file_url
|
||||
window.MathJax.Ajax.StyleString = style_string
|
||||
window.MathJax.Ajax.fileRev = def(file):
|
||||
return ''
|
||||
|
||||
def postprocess(link_uid, proceed):
|
||||
def postprocess(link_uid):
|
||||
for a in document.getElementsByTagName('a'):
|
||||
href = a.getAttribute('href')
|
||||
if href.startswith('#'):
|
||||
a.setAttribute('href', 'javascript: void(0)')
|
||||
a.setAttribute('data-' + link_uid, JSON.stringify({'frag':href[1:]}))
|
||||
proceed()
|
||||
|
||||
def init_mathjax(mathjax_files, link_uid, proceed):
|
||||
monkeypatch(mathjax_files)
|
||||
window.MathJax.Hub.Register.StartupHook("End", postprocess.bind(this, link_uid, proceed))
|
||||
|
||||
def apply_mathjax(mathjax_files, link_uid, proceed):
|
||||
window.MathJax = m = v'{}'
|
||||
m.positionToHash = False
|
||||
m.showMathMenu = False
|
||||
m.showMathMenuMSIE = False
|
||||
m.extensions = "tex2jax.js asciimath2jax.js mml2jax.js".split(' ')
|
||||
m.jax = "input/TeX input/MathML input/AsciiMath output/CommonHTML".split(' ')
|
||||
m.TeX = v'{}'
|
||||
m.TeX.extensions = "AMSmath.js AMSsymbols.js noErrors.js noUndefined.js".split(' ')
|
||||
m.CommonHTML = v'{ linebreaks: { automatic: true} }'
|
||||
document.documentElement.addEventListener("calibre-mathjax-typeset-done", def(ev):
|
||||
postprocess(link_uid)
|
||||
proceed()
|
||||
)
|
||||
script = E.script(type='text/javascript')
|
||||
script.async = True
|
||||
script.src = f'{runtime.FAKE_PROTOCOL}://{runtime.SANDBOX_HOST}/mathjax/startup.js'
|
||||
document.head.appendChild(script)
|
||||
if runtime.is_standalone_viewer:
|
||||
script.onload = def():
|
||||
ev = new CustomEvent("calibre-mathjax-init", {
|
||||
'detail': {
|
||||
'MathJax': m,
|
||||
'link_uid': link_uid,
|
||||
'mathjax_files': mathjax_files,
|
||||
}
|
||||
})
|
||||
document.documentElement.dispatchEvent(ev)
|
||||
document.documentElement.addEventListener("calibre-mathjax-init-done", def(ev):
|
||||
proceed()
|
||||
)
|
||||
else:
|
||||
m.AuthorInit = init_mathjax.bind(this, mathjax_files, link_uid, proceed)
|
||||
script.src = get_url(mathjax_files, 'MathJax.js')
|
||||
|
@ -63,58 +63,9 @@ def get_file(book, name, proceed):
|
||||
xhr.responseType = 'blob'
|
||||
xhr.send()
|
||||
|
||||
def mathjax_file_received(name, proceed, end_type, xhr, ev):
|
||||
end_type = workaround_qt_bug(xhr, end_type)
|
||||
if end_type is 'abort':
|
||||
return
|
||||
if end_type is not 'load':
|
||||
show_error(_('Failed to load MathJax file'), _(
|
||||
'Could not load the file: {} with error: {}').format(name, xhr.error_html))
|
||||
return
|
||||
if not xhr.responseType or xhr.responseType is 'text':
|
||||
result = xhr.responseText
|
||||
else if xhr.responseType is 'blob' or xhr.responseType is 'json':
|
||||
result = xhr.response
|
||||
else:
|
||||
show_error(_('Failed to load MathJax file'), _(
|
||||
'Could not load the file: {} unknown response type: {}')
|
||||
.format(name, xhr.responseType))
|
||||
return
|
||||
if name is 'manifest.json':
|
||||
get_mathjax_files.manifest = result
|
||||
get_mathjax_files_stage2.files_to_get = list(Object.keys(result))
|
||||
get_mathjax_files_stage2.file_data = {}
|
||||
get_mathjax_files_stage2(proceed)
|
||||
return
|
||||
|
||||
get_mathjax_files_stage2.file_data[name] = result
|
||||
get_mathjax_files_stage2.files_to_get.remove(name)
|
||||
if not get_mathjax_files_stage2.files_to_get.length:
|
||||
proceed(get_mathjax_files_stage2.file_data)
|
||||
|
||||
def get_mathjax_manifest(proceed):
|
||||
xhr = ajax('mathjax/manifest.json', mathjax_file_received.bind(None, 'manifest.json', proceed), ok_code=0)
|
||||
xhr.responseType = 'json'
|
||||
xhr.send()
|
||||
|
||||
|
||||
def get_mathjax_files_stage2(proceed):
|
||||
if not get_mathjax_files_stage2.files_to_get.length:
|
||||
proceed(get_mathjax_files_stage2.file_data)
|
||||
return
|
||||
for filename in get_mathjax_files_stage2.files_to_get:
|
||||
xhr = ajax(f'mathjax/{filename}', mathjax_file_received.bind(None, filename, proceed), ok_code=0)
|
||||
xhr.responseType = 'blob'
|
||||
xhr.send()
|
||||
|
||||
|
||||
|
||||
def get_mathjax_files(proceed):
|
||||
if not get_mathjax_files.manifest:
|
||||
get_mathjax_manifest(proceed)
|
||||
else:
|
||||
get_mathjax_files_stage2(proceed)
|
||||
|
||||
proceed({})
|
||||
|
||||
|
||||
def update_url_state(replace):
|
||||
|
Loading…
x
Reference in New Issue
Block a user