このサイトでは数式を本文中に TeX 形式で書いて MathJax で処理させています。↓ こういうやつです。ベクターなので昨今の高解像度事情でもいい感じに綺麗に表示できます。
これはクライアントサイドで本文中の TeX フォーマットを探して全部 SVG とかに置き換えていくのですが、これがなかなか重い処理です。
特にスマートフォンの場合、数式がまともに表示されるまで5秒〜10秒ぐらいかかるので、それまでモヤモヤした感じになります。
さらにいえば、これは非同期で変換をかけているのでロード直後にリフローが頻発することになります。
ということで、これらの問題をなんとかしたいので、標題のようにサーバサイドで MathJax を使ってみるようにしてみました。
このサイトははてな記法で書いたエントリを保存時にHTMLに変換し、変換済みをDBにいれて、表示のときはそれを出しているだけです。なので、保存時に MathJax を通して本文中の数式を埋め込み SVG に変換するようにします。
HTML の断片をうけとって MathJax にかける HTTP サーバ
幸い MathJax-node という node.js 上で動かせる MathJax があったので、これをそのまま使い、http.Server でラップして、ブログシステムとは別に API サーバを立ち上げました。
#!/usr/bin/env node
var mjAPI = require("mathjax-node/lib/mj-page.js");
mjAPI.start();
mjAPI.config({
tex2jax: {
inlineMath: [["\\(","\\)"]],
displayMath: [ ["$$", "$$"] ]
},
extensions: ["tex2jax.js"]
});
var http = require('http');
http.createServer(function (req, res) {
var html = [];
req.on('readable', function () {
var chunk = req.read();
console.log('readable');
if (chunk) html.push(chunk.toString('utf8'));
});
req.on('end', function() {
console.log('end');
console.log('html', html);
mjAPI.typeset({
html: html.join(""),
renderer: "SVG",
inputs: ["TeX"],
ex: 6,
width: 40
}, function (result) {
console.log('typeset done');
console.log(result);
res.writeHead(200, {'Content-Type': 'text/plain; charset=utf-8'});
res.end(result.html);
});
});
}).listen(13370, '127.0.0.1');
console.log('Server running at http://127.0.0.1:13370/');
ブログシステムからは保存時にこのAPIを呼んでHTMLをフィルタしてDBに保存するようにしました。
ハマりどころ
動的な MathJax だと、ページのコンテナサイズから適切な幅を計算してくれるのですが、サーバサイドだとそれができませんので、適切な横幅を自分で指定する必要がありました。
スマートフォンでの閲覧を考えると、width: 40 ぐらいにして、念のため CSS で svg { max-width: 100% } とかを入れておくとよさそうです。width が大きいと、数式ごとのフォントサイズが変わりまくって大変うっとおしいです。
MathJax-node の速度
MathJax-node はなぜかものすごく遅いです。ライブラリロード済みでも10秒ぐらいかかります。そもそも起動も遅いです。オンデマンドでやるのは無理そうなレベルです。
MathJax の JS のロードを抑制
これに伴なって、MathJax を使わなくても良さそうな場合、JSのロードすらやめるようにしました。
所感
MathJax の JS は2段階ぐらいでロードされるので RTT が長いほど不利になります。サーバサイドでやってしまえばJSを転送する必要も実行する必要もなくなるのでエコです。おかげでロードが結構早くなりました。
しかもページロード直後から数式が完全な形で表示されるので、ページに数式がある場合、体感的な速度は数倍に感じます。