数式のエクスポートの話題をしたついでに、HTMLエクスポート時にLaTeX部分を直接MathMLで出力する方法について書いておこうと思います。
というのも10年以上前に同じネタで記事を書いたのですが、とっくの昔にその方法では動かなくなっていました。なので情報を更新しておこうと思いまして。ChromeもMathMLに対応しましたしね。正直対応する日が来るとは思っていませんでした。
org-latex-to-html-convert-commandの怪
それでLaTeX部分のエクスポート形式を変える方法を調べたのですが、今のorg-modeにはorg-latex-to-html-convert-commandというカスタマイズ変数があります。
;; org.elより
(defcustom org-latex-to-html-convert-command nil
"Shell command to convert LaTeX fragments to HTML.
This command is very open-ended: the output of the command will
directly replace the LaTeX fragment in the resulting HTML.
Replace format-specifiers in the command as noted below and use
`shell-command' to convert LaTeX to HTML.
%i: The LaTeX fragment to be converted (shell-escaped).
It must not be used inside a quoted argument, the result of %i
expansion inside a quoted argument is undefined.
For example, this could be used with LaTeXML as
\"latexmlc literal:%i --profile=math --preload=siunitx.sty 2>/dev/null\"."
:group 'org-latex
:package-version '(Org . "9.7")
:type '(choice
(const :tag "None" nil)
(string :tag "Shell command")))
ここにLaTeX表記をHTMLへ変換するコマンドを書いて、後はorg-html-with-latex変数を 'html にすれば(もしくは #+OPTIONS: tex:html を指定すれば)良いらしいです。
……本当に?
(setq org-html-with-latex 'html)
実際やってみるとインラインで書いている部分は変換されるのですが、 \begin{...} ~ \end{...} で書いている部分(environment)は変換されませんでした(Org 9.8時点)。
ox-html.elの中を調べてみると、org-html-latex-fragment関数の中には確かに :with-latex が html であるときの処理が書かれています。しかしorg-html-latex-environment関数の中には :with-latex が html であるときの処理は一切書かれていません!
ナンデ!?
よく分かりませんがなぜかそうなっています。結局org-modeで標準的にサポートしているのはMathjaxを使用した方法だけだと考えるしかないでしょう。メーリングリストを漁れば何か経緯が分かるかもしれませんが面倒なので調べていません。
ちなみにorg-latex-to-mathml-convert-commandという変数もあるのですが、これはODTエクスポート時にしか効果がありません。以前書いた記事では、これをHTMLエクスポート時にも無理矢理使う方法を書きました。でも今更そんな方法でやりたくはありません。
さらに肝心の変換コマンドですが、docstringにはlatexmlcというコマンドを使う方法が書かれています。これはLaTeXMLというPerlで書かれたコンバーターに含まれているものですが、私の環境(MS-Windows)ではうまくインストールできませんでした。頑張れば出来るかもしれませんが、このためだけにStrawberry Perlを入れてCPANがちゃんと動くような環境を維持するのもバカらしいです。
他の方法を色々模索しましたが、最終的にはPandocを使う方法に落ち着いたので今回はそれを書こうと思います。Pandocは入手しやすいですし。
設定
init.elかなにかに次のように書きます。
;; 設定上は`mathjax'にすることによってlatex-environmentでも変換処理をさせる。
;; `org-html-latex-environment'はなぜか`html'を認識しない。
;; `org-html-latex-fragment'は認識する。
(setq org-html-with-latex 'mathjax)
;; mathjax用のHTMLヘッダー(JavaScript)は必要ない。
(setq org-html-mathjax-template "")
;; processing-typeが`mathjax'のときは`html'に書き替えることで
;; `org-format-latex-as-html'関数を無理矢理呼ばせる。
;; 経路:
;; org-html-latex-{environment|fragment}
;; => `org-html-format-latex'
;; => `org-format-latex'
;; => `org-format-latex-as-html'
(defun my-org-format-latex:around (old-fun
prefix &optional beg end dir overlays msg
forbuffer processing-type)
(funcall old-fun prefix beg end dir overlays msg forbuffer
(if (eq processing-type 'mathjax)
'html
processing-type)))
(advice-add #'org-format-latex :around #'my-org-format-latex:around)
;; (advice-remove 'org-format-latex 'my-org-format-latex:around)
;; `org-format-latex-as-html'をpandoc用のものに置き換える。
(defun my-org-format-latex-as-html (latex-fragment)
"Convert LATEX-FRAGMENT to HTML."
(let* ((cmd "pandoc --mathml -f latex")
(html
;; 引数で渡すと問題が起きがちなので標準入力で渡す。
(with-temp-buffer
(insert latex-fragment)
(let ((coding-system-for-read 'utf-8)
(coding-system-for-write 'utf-8)) ;; 私の環境だと文字化け防止に必要
(message "Running %s" cmd)
(shell-command-on-region (point-min) (point-max) cmd nil t))
(buffer-string))))
;; <p>と</p>\nを除去
;; (pandocの変換結果は<p>タグで囲まれている。
;; texmathを使えば囲まないように出来るらしい。
;; 参考: https://news.ycombinator.com/item?id=43883052)
(if (string-match "\\`<p>\\(.*\\)</p>[\n\r ]*\\'" html)
(match-string 1 html)
;; マッチしない。pandocの仕様が変わった?
html)))
(advice-add #'org-format-latex-as-html :override #'my-org-format-latex-as-html)
;; (advice-remove #'org-format-latex-as-html #'my-org-format-latex-as-html)
あとはPandocをインストールしてPATHを通しておいて下さい。
org2blogで使う場合(まさにこのブログ)は org2blog/wp-use-wp-latex を nil にしないとダメだと思います。
結果
これは $x^2$ です(~$ .. $~ を使用)。
これは \( x^2 \) です(~\( .. \)~ を使用)。
これは $$ x^2 $$ です(~$$ .. $$~ を使用)。
これは \[x^2\] です(~\[ .. \]~ を使用)。
\begin{equation}
x^2 + y^2 = r^2
\end{equation}
\begin{gather}
(a+b)^2 = a^2 + 2ab + b^2 \\
(a-b)^2 = a^2 - 2ab + b^2 \\
(a+b)^3 = a^3 + 3a^2b + 3ab^2 + b^3
\end{gather}
以下は実際のエクスポート結果です。
これは です(
$ .. $を使用)。これは です(
\( .. \)を使用)。これは です(
$$ .. $$を使用)。これは です(
\[ .. \]を使用)。
設定の説明
一番の問題はorg-html-latex-environment関数が :with-latex が 'html のときを一切考慮していない点にあります。考慮していないので、フォールバック的な出力、つまりLaTeXのコードをdivやspanで囲んでそのまま出力するという処理になります。考慮しているケースは t または mathjax のとき(デフォルト)と、画像化するとき(org-preview-latex-process-alistのキーであるとき)だけです。
素直に解決しようと思ったらorg-html-latex-environmentにadviceを仕掛けて 'html であるときの処理を追加することになるでしょう。しかしそれだとorg-html-latex-environment関数の頭の部分で行っている共通処理をコピーしなければならなくなります。
なので :with-latex は 'mathjax で使うことにします。その上で、org-format-latex関数にadviceを仕掛け、 'mathjax だったときは 'html に書き替えてしまいます。すると 'mathjax が指定されているのにもかかわらず 'html が指定されていたときの処理であるorg-format-latex-as-htmlが呼び出されるので、後はコマンドラインでLaTeXからMathMLへ変換するだけです。
ただ、今回Pandocで変換するにあたって、org-latex-to-html-convert-commandでは若干指定しづらいところがありました。なのでorg-format-latex-as-html自体を丸丸置き換えて対処しています。頑張ればコマンドラインの指定だけでも何とかできると思いますが、私はシェルスクリプトよりもEmacs Lispの方が得意ですので。
そして最後にorg-html-mathjax-templateを空文字列にしてMathJax用のHTMLヘッダー(JavaScript)が出力されないようにしました。不要ですし、あるとせっかくのネイティブなMathMLがMathJax用の要素に置き換えられてしまいますので。
LaTeXからMathMLへの変換コマンド
今回LaTeXからMathMLへ変換するプログラムとしては次のものを検討しました。
| mathtoweb | 古い。Java依存 |
| LaTeXML | (MS-Windowsで)インストールできなかった |
| Pandoc | 出力の一番外側のpタグとアノテーションを消したい |
| texmath | Pandocが使っているライブラリ。Haskellでのビルドが必要 |
| latex2mathml(python) | 入力の一番外側を処理できない |
他にも無数のプログラムがあるみたいです。
org-latex-to-html-convert-commandに直接指定できるものもありますが、シェルスクリプトでラップしないと厳しいものもあります。
おしまい
それにしてもなんでorg-html-latex-environmentは tex:html に対応していないのか謎すぎますね。fragmentだけでいいからカスタマイズできるようにして! みたいな要望でもあったのでしょうか。……ひょっとして番号付けなどで個別に処理すると正しい結果にならない場合があるからとか? そういえば式番号出てませんね。そもそもちゃんとしたLaTeXの処理系に対してどこまで再現性があるものなのかは分かりません。