定制mathjax构建
mathjax提供了许多组合组件,这些组件以给定的输入和输出格式加载运行mathjax所需的所有内容。不过,您可能会发现,我们提供的任何组件都不完全适合您的需要,并且您希望在构建中包含其他组件,或者可能希望包含自定义配置选项。
您可以使用mathjax组件构建工具来创建您自己的自定义组件,该组件具有您所需的片段和配置。您还可以使用它们来创建自定义扩展,例如tex输入扩展,该扩展利用了已加载的组件,但实现了其他功能。这些可能性在 构建自定义组件 下面。
还可以对mathjax进行完全自定义的构建,该构建完全不使用mathjax组件,但包含对mathjax源文件的直接调用。这在 自定义mathjax构建 下面。
如果您希望将mathjax包含在一个更大的项目中,可以使用这两种技术中的任何一种,并制作一个包含您自己的项目代码和mathjax的网页文件。
准备好东西
第一步是通过 npm
或 git
,如 获取mathjax代码 .
如果你使用
npm
,您将希望安装mathjax-full
包而不是mathjax
包,因为前者包括所有的源代码,包括其原始和编译的形式,以及webpacket组件。
如果你使用
git
请确保编译和组件中列出的命令 通过git获取mathjax .
无论哪种情况,你都应该 js
,一个 es5
和A components
目录,在 node_modules/mathjax-full
目录(用于 npm
安装)或主目录(用于 git
安装)。
第二步是使用 webpack
. 使用命令
npm install webpack
npm install webpack-cli
npm install terser-webpack-plugin
npm install babel-loader
npm install @babel/core
npm install @babel/preset-env
安装 webpack
以及所需的库。一旦完成了这一步,您应该能够制作下面描述的组件。建筑说明假设你使用了 npm
如果你用 git
,则需要删除 node_modules/mathjax-full
从包围他们的道路上。
构建自定义组件
mathjax附带了许多预定义的组件,您可以使用 their definitions 作为自定义组件的起点。在 MathJax web demos repository ,与这里描述的相似。
您可以构建两种组件:
A 组合元件 将其他几个组件(
tex-chtml
组件是组合组件)
A 扩展组件 它包含一个特性所需的内容,可以与其他组件一起加载,以便将该特性添加到mathjax中。
我们将在下面描述如何创建每一个。在这两种情况下,都应该创建一个目录来保存组件的支持文件。您将需要组件的主控制文件(包括定义组件的代码)和一个webpack控制文件,该文件将告诉mathjax的构建工具如何处理组件。这些将在下面的章节中讨论。
自定义组合组件
按照 准备好东西 ,为组件创建目录并 cd
到那个目录。我们假设这个目录名为 custom-mathjax
为了这次讨论。
对于本例,我们将创建一个具有tex input jax和svg output jax的自定义构建,并加载 newcommand
, ams
和 configmacros
扩展,但不包括 require
或 autoload
,因此用户将无法加载任何其他tex扩展。此组件还包括上下文菜单。
控制文件
创建一个javascript文件来存放组件并调用它 custom-mathjax.js
. 文件应该包含以下代码(我们假设您使用 npm
安装mathjax。如果没有,您可能需要调整 require()
命令)。
//
// Initialize the MathJax startup code
//
require('mathjax-full/components/src/startup/lib/startup.js');
//
// Get the loader module and indicate the modules that
// will be loaded by hand below
//
const {Loader} = require('mathjax-full/js/components/loader.js');
Loader.preLoad(
'loader', 'startup',
'core',
'input/tex-base',
'[tex]/ams',
'[tex]/newcommand',
'[tex]/configmacros',
'output/svg', 'output/svg/fonts/tex.js',
'ui/menu'
);
//
// Load the components that we want to combine into one component
// (the ones listed in the preLoad() call above)
//
require('mathjax-full/components/src/core/core.js');
require('mathjax-full/components/src/input/tex-base/tex-base.js');
require('mathjax-full/components/src/input/tex/extensions/ams/ams.js');
require('mathjax-full/components/src/input/tex/extensions/newcommand/newcommand.js');
require('mathjax-full/components/src/input/tex/extensions/configmacros/configmacros.js');
require('mathjax-full/components/src/output/svg/svg.js');
require('mathjax-full/components/src/output/svg/fonts/tex/tex.js');
require('mathjax-full/components/src/ui/menu/menu.js');
//
// Update the configuration to include any updated values
//
const {insert} = require('mathjax-full/js/util/Options.js');
insert(MathJax.config, {
tex: {
packages: {'[+]': ['ams', 'newcommand', 'configmacros']}
}
});
//
// Loading this component will cause all the normal startup
// operations to be performed
//
require('mathjax-full/components/src/startup/startup.js');
这将加载我们希望包含在组合组件中的各种组件,包括标准启动代码,以便包含通常的启动过程。
网页包配置
接下来,创建文件 webpack.config.js
包括以下内容:
const PACKAGE = require('mathjax-full/components/webpack.common.js');
module.exports = PACKAGE(
'custom-mathjax', // the name of the package to build
'../node_modules/mathjax-full/js', // location of the mathjax library
[], // packages to link to
__dirname, // our directory
'.' // where to put the packaged component
);
此文件提供将用于此组件的名称 (custom-mathjax
在本例中),指向要找到mathjax javascript代码的位置的指针(调整此项以适应您的设置),我们假设在加载此项时已加载的组件数组(在本例中为“无”),我们正在处理的目录名(始终 __dirname
,以及我们希望最终打包组件所在的目录(默认为 mathjax-full/es5
目录,但我们将其设置为包含源文件的目录,组件将以 .min.js
)
大部分真正的工作是由 mathjax-full/components/webpack.common.js
文件,它包含在第一行。
构建组件
一旦这两个文件准备好,就可以构建组件了。首先,确保您已获得所需的工具,如中所述 准备好东西 以上。那么您应该能够使用命令
node ../node_modules/mathjax-full/components/bin/makeAll
以处理自定义生成。你最后应该得到一个文件 custom-mathjax.min.js
与其他文件在目录中。如果你把它放在你的web服务器上,你可以把它加载到你的web页面中,而不是从CDN加载MathJax。这个文件将包含在页面上运行MathJax所需的所有内容。只要加上
<script src="custom-mathjax.min.js" id="MathJax-script" async></script>
到您的页面,您应该在业务中(调整url以指向您放置 custom-mathjax.min.js
文件)。
配置组件
请注意,您仍然可以包括 MathJax = {{...}}
如果要自定义特定页的配置,请在加载此自定义mathjax生成之前在网页中定义。您还可以在组件本身中包含配置,就像我们对tex所做的那样 packages
数组。但是,这将覆盖任何页面提供的配置,因此如果要提供仍然可以在页面中覆盖的非标准默认值,请使用
//
// Update the configuration to include any updated values
//
const {insert} = require('mathjax-full/js/util/Options.js');
insert(MathJax.config, {tex: {packages: {'[+]': ['ams', 'newcommand', 'configmacros']}}}, false);
MathJax.config = insert({
// your default options here
}, MathJax.config);
它将更新tex包,然后将用户的配置选项合并到默认值并设置 MathJax.config
合并选项。
CommonHTML字体
如果在自定义构建中包含commonhtml输出jax,则实际的web字体不会包含在web包文件中,因此可能需要包括 fontURL 在 chtml 配置的块,并让它提供一个可以找到字体的url。他们在 mathjax-full/es5/output/chtml/fonts/woff-v2
目录,您可以将它们放在服务器上,或者直接指向 fontURL 到字体的cdn目录之一。
自定义扩展
创建自定义扩展与创建自定义组合组件非常相似。主要的区别在于扩展可能依赖于其他组件,所以您需要告诉构建系统这一点,这样它就不会包含来自其他组件的代码。您也不会直接加载扩展文件(就像上面的组合组件一样),而是将其包含在 load 数组 loader 配置块,mathjax自己加载它,如下所述。
对于这个例子,我们创建了一个自定义的tex扩展,它定义了由javascript函数实现的新tex命令。
这里实现的命令提供了从TeX中手动生成MathML令牌元素的能力。这允许对生成的元素的内容和属性进行更多的控制。宏是 \mi
, \mo
, \mn
, \ms
和 \mtext
,它们各自接受一个参数,该参数是要用作相应mathml元素内容的文本。文本不由tex进一步处理,但扩展名会转换表单的序列 \uNNNN
(在那里 N
是十六进制数字)转换成相应的Unicode字符;例如, \mi{{\u2460}}
会产生u+2460,一个带圆圈的数字1,作为 mi
元素。
扩展文件
按照 准备好东西 ,为名为 custom-extension
和 cd
去吧。然后创建文件 mml.js
包含以下文本:
import {Configuration} from '../node_modules/mathjax-full/js/input/tex/Configuration.js';
import {CommandMap} from '../node_modules/mathjax-full/js/input/tex/SymbolMap.js';
import TexError from '../node_modules/mathjax-full/js/input/tex/TexError.js';
/**
* This function prevents multi-letter mi elements from being
* interpreted as TEXCLASS.OP
*/
function classORD(node) {
this.getPrevClass(node);
return this;
}
/**
* Convert \uXXXX to corresponding unicode characters within a string
*/
function convertEscapes(text) {
return text.replace(/\\u([0-9A-F]{4})/gi, (match, hex) => String.fromCharCode(parseInt(hex,16)));
}
/**
* Allowed attributes on any token element other than the ones with default values
*/
const ALLOWED = {
style: true,
href: true,
id: true,
class: true
};
/**
* Parse a string as a set of attribute="value" pairs.
*/
function parseAttributes(text, type) {
const attr = {};
if (text) {
let match;
while ((match = text.match(/^\s*((?:data-)?[a-z][-a-z]*)\s*=\s*(?:"([^"]*)"|(.*?))(?:\s+|,\s*|$)/i))) {
const name = match[1], value = match[2] || match[3]
if (type.defaults.hasOwnProperty(name) || ALLOWED.hasOwnProperty(name) || name.substr(0,5) === 'data-') {
attr[name] = convertEscapes(value);
} else {
throw new TexError('BadAttribute', 'Unknown attribute "%1"', name);
}
text = text.substr(match[0].length);
}
if (text.length) {
throw new TexError('BadAttributeList', 'Can\'t parse as attributes: %1', text);
}
}
return attr;
}
/**
* The mapping of control sequence to function calls
*/
const MmlMap = new CommandMap('mmlMap', {
mi: ['mmlToken', 'mi'],
mo: ['mmlToken', 'mo'],
mn: ['mmlToken', 'mn'],
ms: ['mmlToken', 'ms'],
mtext: ['mmlToken', 'mtext']
}, {
mmlToken(parser, name, type) {
const typeClass = parser.configuration.nodeFactory.mmlFactory.getNodeClass(type);
const def = parseAttributes(parser.GetBrackets(name), typeClass);
const text = convertEscapes(parser.GetArgument(name));
const mml = parser.create('node', type, [parser.create('text', text)], def);
if (type === 'mi') mml.setTeXclass = classORD;
parser.Push(mml);
}
});
/**
* The configuration used to enable the MathML macros
*/
const MmlConfiguration = Configuration.create(
'mml', {handler: {macro: ['mmlMap']}}
);
注释解释了这段代码在做什么。使其成为tex扩展所需的主要部分是 Configuration
在最后几行中创建。它创建一个名为 mml
通过 CommandMap
已命名 mmlMap
它的定义就在它上面。该命令映射定义了本节开头描述的五个宏,每个宏都与一个名为 mmlToken
在下面的对象中,向它传递要创建的MathML标记元素的名称。这个 mmlToken
方法是当 \mi
其他宏被调用。它获取宏的参数和任何可选属性,并使用这些属性创建mathml元素,将参数用作元素的文本。
网页包配置
接下来,创建文件 webpack.config.js
包括以下内容:
const PACKAGE = require('mathjax-full/components/webpack.common.js');
module.exports = PACKAGE(
'mml', // the name of the package to build
'../node_modules/mathjax-full/js', // location of the mathjax library
[ // packages to link to
'components/src/core/lib',
'components/src/input/tex-base/lib'
],
__dirname, // our directory
'.' // where to put the packaged component
);
此文件提供将用于此组件的名称 (mml
在本例中),指向要找到mathjax javascript代码的位置的指针(调整此项以适应您的设置),我们假设在加载此项时已经加载了一个组件数组(在 core
和 tex-base
在本例中是组件),我们正在使用的目录名(总是 __dirname
,以及我们希望最终打包组件所在的目录(默认为 mathjax-full/es5
目录,但我们将其设置为包含源文件的目录,组件将以 .min.js
)
大部分真正的工作是由 mathjax-full/components/webpack.common.js
文件,它包含在第一行。
扩建工程
一旦这两个文件准备好,就可以构建组件了。首先,确保您已获得所需的工具,如中所述 准备好东西 以上。那么您应该能够使用命令
node ../node_modules/mathjax-full/components/bin/makeAll
以处理自定义生成。你最后应该得到一个文件 mml.min.js
与其他文件在目录中。如果将其放到Web服务器上,则可以通过将其放入 load
数组 loader
配置块,如下所述。
加载扩展名
要加载自定义扩展名,需要告诉mathjax它的位置,并将其包含在启动时要加载的文件中。mathjax允许您定义到存储扩展的位置的路径,然后可以使用表示该位置的前缀来引用该位置中的扩展。mathjax有一个预定义的前缀, mathjax
这是显式指定none时的默认前缀,它引用加载主mathjax文件的位置(例如,文件 tex-svg.js
或 startup.js
)
通过使用 paths
对象中 loader
配置的块。在我们的例子中(见下面的代码),我们添加了 custom
前缀,并让它指向扩展名的url(在本例中,与加载它的html文件相同的目录,由url表示 .
)我们使用 custom
要指定的前缀 [custom]/mml.min.js
在 load
数组以便加载扩展。
最后,我们添加 mml
扩展到 packages
数组中 tex
通过特殊符号的配置块 {{'[+]': [...]}}
告诉mathjax将给定数组附加到现有的 packages
默认情况下已在配置中的数组。所以这将使用所有已经指定的包,以及 mml
在扩展中定义的包。
mathjax的配置和加载现在如下所示:
<script>
MathJax = {
loader: {
load: ['[custom]/mml.min.js'],
paths: {custom: '.'}
},
tex: {
packages: {'[+]': ['mml']}
}
};
</script>
<script type="text/javascript" id="MathJax-script" async
src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js">
</script>
你应该换个 custom: '.'
指向服务器的实际URL的行。
此示例加载 tex-chtml.js
组合组件,因此在加载扩展时已加载tex输入。如果你正在使用 startup.js
相反,包括 input/tex
在 load
数组,您需要告诉mathjax您的扩展依赖于 input/tex
扩展,以便它等待加载扩展,直到加载tex输入jax之后。为此,添加 dependencies
阻止您的配置,如下所示:
<script>
MathJax = {
loader: {
load: ['input/tex', 'output/chtml', '[custom]/mml.min.js'],
paths: {custom: '.'},
dependencies: {'[custom]/mml.min.js': ['input/tex']}
},
tex: {
packages: {'[+]': ['mml']}
}
};
</script>
<script type="text/javascript" id="MathJax-script" async
src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/startup.js">
</script>
这个例子可以在 MathJax 3 demos 储存库。
自定义mathjax构建
完全可以定制一个完全不基于其他mathjax组件的mathjax构建。下面的示例演示如何生成自定义生成,该生成提供一个函数,用于获取给定tex数学字符串的语音字符串。这个例子类似于 MathJax3 demos 储存库。
按照 准备好东西 ,创建名为 mathjax-speech
和 cd
投入其中。
自定义生成文件
创建名为 mathjax-speech.js
包含以下内容:
//
// Load the desired components
//
const mathjax = require('mathjax-full/js/mathjax.js').mathjax; // MathJax core
const TeX = require('mathjax-full/js/input/tex.js').TeX; // TeX input
const MathML = require('mathjax-full/js/input/mathml.js').MathML; // MathML input
const browser = require('mathjax-full/js/adaptors/browserAdaptor.js').browserAdaptor; // browser DOM
const Enrich = require('mathjax-full/js/a11y/semantic-enrich.js').EnrichHandler; // semantic enrichment
const Register = require('mathjax-full/js/handlers/html.js').RegisterHTMLHandler; // the HTML handler
const AllPackages = require('mathjax-full/js/input/tex/AllPackages').AllPackages; // all TeX packages
const STATE = require('mathjax-full/js/core/MathItem.js').STATE;
const sreReady = require('mathjax-full/js/a11y/sre.js').sreReady(); // SRE promise;
//
// Register the HTML handler with the browser adaptor and add the semantic enrichment
//
Enrich(Register(browser()), new MathML());
//
// Initialize mathjax with a blank DOM.
//
const html = MathJax.document('', {
sre: {
speech: 'shallow', // add speech to the enriched MathML
},
InputJax: new TeX({
packages: AllPackages.filter((name) => name !== 'bussproofs'), // Bussproofs needs an output jax
macros: {require: ['', 1]} // Make \require a no-op since all packages are loaded
})
});
//
// The user's configuration object
//
const CONFIG = window.MathJax || {};
//
// The global MathJax object
//
window.MathJax = {
version: mathjax.version,
html: html,
sreReady: sreReady,
tex2speech(tex, display = true) {
const math = new html.options.MathItem(tex, inputJax, display);
math.convert(html, STATE.CONVERT);
return math.root.attributes.get('data-semantic-speech') || 'no speech text generated';
}
}
//
// Perform ready function, if there is one
//
if (CONFIG.ready) {
sreReady.then(CONFIG.ready);
}
与上面基于组件的示例不同,此自定义构建直接调用mathjax源文件。这个 require
文件开头的命令加载所需的对象,其余代码指示mathjax创建 MathDocument
对象来处理我们将要进行的转换(使用tex input jax),然后定义一个全局 MathJax
对象具有 tex2speech()
我们定制的功能。
网页包配置
接下来,创建文件 webpack.config.js
包括以下内容:
const PACKAGE = require('mathjax-full/components/webpack.common.js');
module.exports = PACKAGE(
'mathjax-speech', // the name of the package to build
'../node_modules/mathjax-full/js', // location of the mathjax library
[], // packages to link to
__dirname, // our directory
'.' // where to put the packaged component
);
此文件提供将用于此组件的名称 (mathjax-speech
在本例中),指向要找到mathjax javascript代码的位置的指针(调整此项以适应您的设置),我们假设在加载此组件时已经加载了一个组件数组(无,因为这是一个自包含的构建),我们正在使用的目录名(始终 __dirname
,以及我们希望最终打包组件所在的目录(默认为 mathjax-full/es5
目录,但我们将其设置为包含源文件的目录,组件将以 .min.js
)
大部分真正的工作是由 mathjax-full/components/webpack.common.js
文件,它包含在第一行。
生成自定义文件
一旦这两个文件准备就绪,就可以进行自定义生成了。首先,确保您已获得所需的工具,如中所述 准备好东西 以上。那么您应该能够使用命令
node ../node_modules/mathjax-full/components/bin/makeAll
以处理自定义生成。你最后应该得到一个文件 mathjax-speech.min.js
与其他文件在目录中。它将只包含实现 MathJax.tex2speech()
上面文件中定义的命令。注意,这不足以进行正常的排版(例如,没有包含输出jax),因此这是一个用于从tex输入生成语音字符串的最小文件。
在网页中使用文件
如果你把 mathjax-speech.min.js
文件在web服务器上,您可以将其加载到web页面中,而不是从cdn加载mathjax。此填充将包括所有您需要使用的 MathJax.tex2speech()
命令在你的页面上。只要添加
<script src="mathjax-speech.min.js" id="MathJax-script" async></script>
到您的页面(调整url以指向您放置 custom-mathjax.min.js
文件)。然后可以使用javascript调用
const speech = MathJax.tex2speech('\\sqrt{x^2+1}', true);
获取一个文本字符串,该字符串包含tex字符串中给定的平方根的语音文本。
但是,请注意,作为语音生成基础的语音规则引擎(sre)是异步加载的,因此在进行此类调用之前,必须确保sre已准备就绪。这个 mathjax-speech.js
文件提供了两种处理与sre同步的方法。首先是使用全局 MathJax
变量包括 ready()
当sre准备好时调用的函数。例如,
window.speechReady = false;
window.MathJax = {
ready: () => {
window.speechReady = true;
}
};
将设置全局变量 speechReady
当sre准备运行时为true(因此您可以检查该值以查看是否可以生成语音)。更复杂的 ready()
函数可以让您将要执行的转换排队,当sre准备好时,它将执行这些转换。或者,如果您有一个允许用户转换tex表达式的用户界面,那么您可以首先禁用触发语音生成的按钮,然后使用 ready()
功能来启用它们。这样,用户就不能要求语音翻译,直到它可以产生。
与sre同步的第二种方法是通过以下事实 MathJax.sreReady
当sre准备好的时候,这个承诺就解决了,你可以用它来确保sre准备好了,当你想做语音生成的时候。例如
function showSpeech(tex, display = false) {
MathJax.sreReady = MathJax.sreReady.then(() => {
const speech = MathJax.tex2speech(tex, display);
const output = document.getElementById('speech');
output.innerHTML = '';
output.appendChild(document.createTextNode(speech));
});
}
提供一个函数,该函数允许您指定要转换的tex字符串,然后(异步)为其生成语音并将其显示为 id="speech"
在页面中。