これは「TeX & LaTeX Advent Caleandar 2022」の14日目の記事です。
(13日目はdoratex さんでした。15日目はh20y6m さんです。)
更新履歴
- 2022-12-21: 微修正,次の日のリンクと参考記事を追加
- 2022-12-15: native-sub.luaを追加
- 2022-12-14: 初版公開(Pandoc nativeのSubscript以外の部分について)
TeX & LaTeX Advent Caleandar 2022の重点テーマは,「やっぱりTeX言語(とか)しましょう!」ということですが,
以下のいずれかテーマに該当する何かを書きます。
- 「やっぱりTeX言語(とか)しましょう!」に大いに関連するTeX/LaTeXネタ。
- 「やっぱりTeX言語(とか)しましょう!」にチョット関連するTeX/LaTeXネタ。
- 「やっぱりTeX言語(とか)しましょう!」にサッパリ関連しないTeX/LaTeXネタ。
ということですので今回はTeX言語にはサッパリ関係しない,Markdown原稿からPandocを用いてWord/LaTeX出力した1ときに両方に対応するためにLuaフィルターを書いたりしたことについて書いてみます。
いくつかの例を示しますが,特に(記事公開時点では)上付き/下付き文字の扱いについて考えます。
その前に,私が実際にお世話になったLuaフィルターを紹介しましょう。Pandoc公式フィルター集の中のpagebreakは,本来LaTeXでのみ有効である \pagebreak
や \newpage
を各出力先に対応させてくれます。AsciiDoc/Asciidoctor,ConTeXt,Docx,EPUB,groff ms,HTML,LaTeXに対応しているそうで,実装からも出力フォーマットに応じて変換を掛けていることがわかります。これを利用することで,Markdown原稿→Word出力でも簡単に改ページが実現できました。
Markdown原稿を変換してみる
ここでは,以下の文章をMarkdownで記述し,LaTeXに変換することを考えましょう。
エチレンC2H6を付加重合するとポリエチレン(C2H4)nができる。組成式(C6H11ON)nであらわさられるナイロン6はε-カプロラクタムから合成される。
酸素欠損δがあるペロブスカイトは化学式ABO3−δであらわされる。
さて,ここで科学系で頻出の下付き文字をどうMarkdownで表現するかですが,ここでは素直に <sub> タグ等を用いて表記することとし,以下ではその前提で記述を進めます。
もちろん, <sub>subscript</sub>
のようにHTMLタグを使用する以外にもいくつかの選択肢が考えられますが,テキストエディタのビルトインのpreviewerで対応できること,またGitHub上でそのまま表示できることを優先し,ここでは(記法はやや煩雑ですが)HTMLタグを使用した方法をとることとします。
他の記法としては,次のようなものが考えられます。
- Pandocの
subscript
/superscirpt
拡張を有効にする:pandoc実行時に--from=markdown+subscript+superscript
のように指定することでH~2~O(→H2O)のような記法を有効にできます - 数式要素を使って上付き/下付き文字を表現することもできそうですが2,ここでは扱いません。
この前提に立つと,上記の文章は以下のようなMarkdown文書で記述できます。
エチレンC<sub>2</sub>H<sub>6</sub>を付加重合するとポリエチレン(C<sub>2</sub>H<sub>4</sub>)<sub>*n*</sub>ができる。組成式(C<sub>6</sub>H<sub>11</sub>ON)<sub>*n*</sub>であらわさられるナイロン6は*ε*-カプロラクタムから合成される。
酸素欠損*δ*があるペロブスカイトは化学式*AB*O<sub>3−*δ*</sub>であらわされる。
では,PandocでWordおよびLaTeXに変換してみましょう。以下を実行してみます。
pandoc sample.md -o sample.tex
pandoc sample.md -o sample.docx
すると,生成されたLaTeXファイルとそれを変換したPDF,およびWordファイルは以下のようになります。
エチレンC2H6を付加重合するとポリエチレン(C2H4)\emph{n}ができる。組成式(C6H11ON)\emph{n}であらわさられるナイロン6は\emph{ε}-カプロラクタムから合成される。
酸素欠損\emph{δ}があるペロブスカイトは化学式\emph{AB}O3−\emph{δ}であらわされる。
いずれも下付き文字が反映されていませんね......。どうやらPandocではデフォルトではHTMLタグで書かれた下付き文字は単に無視されてしまうようです3。
Part1: LaTeX出力で下付き文字が反映されるようにしてみる
LaTeXの本文中で下付き文字を表記するには, \textsubscript{}
が利用できます。そこで,まずはやや場当たり的ですが以下のようなLuaフィルターを用いて<sub>
タグを\textsubscript{
に,</sub>
タグを}
に変換することにしましょう4(公式例や@Atsushi776さんのPandoc Lua Filtersのreturnの挙動と複数のフィルタを書くときの用例を参考にしました)。
function SubOpen(elem)
if FORMAT:match 'latex' and elem.text == "<sub>" then
return pandoc.RawInline('latex', string.gsub(elem.text, "<sub>", "\\textsubscript{"), "")
end
end
function SubClose(elem)
if FORMAT:match 'latex' and elem.text == "</sub>" then
return pandoc.RawInline('latex', string.gsub(elem.text, "</sub>", "}"))
end
end
return {
{RawInline = SubOpen},
{RawInline = SubClose}
}
ここでは,出力フォーマットがLaTeXである場合に,Pandoc ASTのInline要素(?)が特定のタグである場合に代わりにLaTeXの命令文字列に置換して返すような動作をしています。
実際にPandocの変換とLuaLaTeXによるタイプセットを実行してみましょう。subscipt.lua
は同じディレクトリに置いておき,以下のようにオプションで指定します。
pandoc sample.md --lua-filter=subscript.lua -o sample.tex
lualatex.exe sampele-index.tex
(ここでは便宜上以下のLaTeXファイルから\input
でsample.tex
を読み込んでいます)
\documentclass[
article,
paper=a4,
onecolumn,
lualatex
]{jlreq}
\usepackage{hyperref, amsmath, fontspec, graphicx, framed, fancyvrb, color, ifthen}
\usepackage{unicode-math}
\setmainfont{Times New Roman}
\ltjsetparameter{jacharrange={-2,-3}}%ギリシャ文字を欧文フォント扱いに
\begin{document}
\input{sample.tex}%ここで生成されたLaTeXファイルを読み込む
\end{document}
エチレンC\textsubscript{2}H\textsubscript{6}を付加重合するとポリエチレン(C\textsubscript{2}H\textsubscript{4})\textsubscript{\emph{n}}ができる。組成式(C\textsubscript{6}H\textsubscript{11}ON)\textsubscript{\emph{n}}であらわさられるナイロン6は\emph{ε}-カプロラクタムから合成される。
酸素欠損\emph{δ}があるペロブスカイトは化学式\emph{AB}O\textsubscript{3−\emph{δ}}であらわされる。
実際に以下のようなPDFファイルを生成することができます。
Part2: Word出力でも下付き文字に変更したい
前節ではややアドホックに<sub>
タグを置換することでLaTeX出力で下付き文字を実現しました。しかし,この方法でWord出力にも対応しようとするとOffice Open XMLをいじらなくてはいけなくなります(pagebreak.luaのように)。
ここでは方針を変え,Pandocのネイティブの下付き文字の形式に変換してしまうことで個別の出力形式を考えなくて済むようにしようと思います。実際,以下のようにPandocではASTレベルでSubscriptがちゃんと用意されています。
echo "H~2~O" | pandoc --from=markdown+subscript --to=native
# => [ Para [ Str "H" , Subscript [ Str "2" ] , Str "O" ] ]
ということで,<sub>
タグが現れたらその中身をSubscript
に包んで返してやるLuaフィルターを実装すればよいことになります。以下に(あまりきれいではないですが)実装例を示します。(もうちょっとシュッと書けないかな...)
function Inlines(inlines)
local start = 1
while start < #inlines do
if (inlines[start].text == '<sub>') then
local len = 1
while (start + len <= #inlines) do
if (inlines[start + len].text == '</sub>') then
local inner = {
table.unpack(inlines, start + 1, start + len)
}
for j = 1, len do inlines:remove(start) end
inlines:insert(start, pandoc.Subscript(inner))
break
end
len = len + 1
end
end
start = start + 1
end
return inlines
end
LaTeXは前節と同様の出力となります。さらに,Wordでも画像のように下付き文字が有効になりました。
基本的にはインラインの要素を走査してタグをPandoc ASTのSupscriptに置き換えているだけですが,あまり詳しい解説を書いている時間がないのでまた後日追記しようと思います!(えっ
まとめ
下付き文字の例だけだとLuaフィルターを使うにはややメリットが弱いかなぁと思いますが(中間ファイルの生成などを許容すれば単純なテキスト置換でも実現できてしまいます),冒頭に紹介したpagebreakや,Markdownに対するプリプロセス・LaTeXファイルに対するポストプロセスのようにばらけてしまう処理であってもLuaフィルターに入れ込んでしまってPandocコマンド一発で処理できるのは便利だと思います。
もちろん,ここではLuaフィルターのごく一部を使っているのみですので,他に実現したい機能がある場合にはLuaフィルターは強力なツールとなるでしょう。最後になりますが,私が参考にしたPandoc・Luaフィルター関連の記事・ドキュメントの一部を紹介します。
- Pandoc公式ドキュメント日本語版およびLuaフィルターについての公式ドキュメント
- @atusyさんのAtusy's blog(Luaカテゴリ),Luaフィルターのコレクション
-
sky-yさんのLuaフィルター集・その他記事(Zoteroから参考文献リストを自動エクスポートする (PandocとZoteroで参考文献:前編)
など)
-
みなさんは日本語の長めの文書,特に修士論文などを書くときにどのようなツール・形式を選択するでしょうか。いくつか選択肢はあると思いますが,Markdownを選択することは以下のようなメリットがあると考えられます:テキスト形式なのでバージョン・差分管理と相性がよい/記法が比較的簡潔で読みやすい/GitHubの対応しているサービスにそのままおいておけばプレビュー表示してくれる
さて,修士論文では最終的にはA4ページに組まれたPDFとして成果物を生成することになりますが,ここでMarkdownからなんらかの形でPDFに変換することが必要になりますが,今回はPandocでLaTeX形式に変換した後にLuaLaTeXを用いて組むこととしました。この点はすでにいろいろな事例がWeb上で共有されていることかと思います。Pandocはさまざまな形式に対応していますし,LaTeXの柔軟かつ美しい組版は最終出力に適していると思います。
一方,例えば修士論文なんかの場合は複数回のレビューをしてもらう必要がありますが,この点ではMicrosoftのWord(コメント機能とか)が適していることもあるでしょう。記事の後半ではWordへの対応も考えてみる予定です。 ↩ -
数式要素は単に通常の本文テキスト中の上付き/下付き文字を組むのには適さないと思われます。わかりやすいところだとLaTeXの数式環境やWordの数式ツールに変換されてしまいますのでフォントなどが変わってしまうし,数式と地の文を統一的に扱えるLaTeX出力を念頭におくならいわゆる数式環境かどうかはどちらでもよいかと思いますが,MarkdownプレビューやWordで地の文と異なるフォントが適用されたり,そもそもプレーンなMarkdownビューワーではMathJax等による数式表示機能が備わっていないことを考慮すると(数式ではなく)地の文で表記するのがいいと判断される場合もあるかと思います。(2022-12-23追記) ↩\text
などを使用しないといけないのもあまり筋がよいとは思えません。 -
Pandoc拡張のsubscript/superscriptやraw_htmlなどを有効にしても動作は変わりませんでした。オプション一つで
<sub>
タグをPandoc ASTのSubscriptに変換できるならこの記事ほとんどいらないのでどなたかコメントください...!! ↩ -
単純にMarkdownからテキスト置換を行うプリプロセスを行ってPandoc subscript拡張形式(H~2~O)で表記された拡張中間ファイルを生成し,
--format=markdown+subscript
オプションをつけてPandoc処理を行うことでももちろん可能です。が,せっかくなのでLuaフィルターを使ってみましょう。 ↩