LaTeX Tools
これは、LaTeX & TeX Advent Calendarの21日目の記事です。20日目は、えぬぬん氏の「どうしてLaTeXを使うのか、もう一度考えてみる」でした。22日目はpuripuri2110氏の「texconf2019の供養」です。
この記事は、LaTeX-BuildToolsとLaTeX-SubsToolsという書籍作成を支援するツールについて書いたものです。ツールそのものの解説ではなく、そのバックグラウンドを説明しています。ツールの使い方については各githubレポジトリのReadme.mdを参照してください。 なお、この2つのツール群を総称して「LatexTools」と呼ぶこともあります。
コンピュータの環境について
ここで述べることは、LinuxディストリビューションのDebian(stretch)の上での話です。 Windowsなどでも同じ発想でシステムを考えることは可能ですが、ここで紹介するスクリプト自体は動きません。 ご注意ください。
LaTeXによる書籍作成
ここでは、LaTeXによるPDF形式の(電子)書籍の作成とその支援ツールについて考えます。 この書籍作成は、もう少し具体的にいうと、LaTeXのドキュメントクラスでbookを指定してPDFを作成することです。
私の最近のLaTeX使用状況は、おそらく多くの読者とは異なっています。 というのは、昨年の1月から2年間、海外に住むことになり、数学教育のボランティアをしているからです。 こちらの国では数学の本が極めて少なく、教材を作る必要にせまられて、テキストを数冊作りました。 しかし、印刷はとてもお金がかかるので、PDF形式のデジタル配布です。 作成にはLaTeXを使いました。 エンジンはpdflatexまたはlualatexでしたが、表紙に大きな文字を使いたかったので、フォントサイズの指定できるlualatexを最終的には使いました。
そのような状況ですので、日本語の組版はほとんどしておりません。 したがって、ここで述べることは、日本語の組版でも通用するとは思いますが、未確認であり、上手く動作しない可能性もあります。
どのようにソースファイルを記述するか
ソースファイルはLaTeXの文法に合っていればどのように書いてもコンパイルは通ります。 例えば、別行の数式を書く場合を考えてみます。
\begin{eqnarray}y&=&x^2+x+1\label{eq:1}\\y&=&1\label{eq:2}\end{eqnarray}
これは次のように4行に分けて書く人が多いと思います。
\begin{eqnarray}
y&=&x^2+x+1\label{eq:1}\\
y&=&1\label{eq:2}
\end{eqnarray}
2行目と3行目にはインデントをつけていますが、つけない主義の人もいるかもしれません。 はじめに書いたようにどれでもコンパイルは通るので、latexから見ての優劣はありません。 しかし、書籍のような大きな文書を作る場合は、どのように記述するかを良く考えて、一貫した方法で記述することが望ましいです。 少なくとも、どちらかの方法に統一することです。
そのように述べる理由は、修正が容易になるということです。 ソースファイルの修正の方法は大きく2通りあります。
- 手作業で修正する
- 何らかの言語のスクリプトを作ってコンピュータに修正させる
それぞれに利点がありますが、大きな文書の場合は、後者を使うことが非常に効果を発揮します。 その際、ソースファイルの書き方に一貫したものがあれば、その変更をするスクリプトを書くのは容易です。 逆に、いろいろなスタイルで書いてしまうと、どの書き方にも対応するスクリプトを書かなければなりません。 最悪の場合、TeXの字句解析や構文解析と同じようなことをしなければならなくなります。 それはそれで、勉強にはなると思いますが、やりたい人は少ないでしょう。
さきほどの例ですが、いつも前者の書き方で、1行の中に1つのeqnarray環境を書いているとします。 eqnarray環境はいろいろ問題があって、align環境を勧める人が多いです。 それについては、ちょっとググれば情報が入手できます。 それで、すべてのeqnarray環境をalign環境に書き改めたくなったとします。 スクリプト言語にはrubyを使うとします。 取り出した1行の文字列sに対して、
if s =~ /\\begin\{eqnarray\}.*\\end\{eqnarray\}/
とすれば、eqnarray環境を検索できます。 align環境に置き換えるには、
s.gsub(/eqnarray/,"align").gsub(/&=&/,"&=")
などとすれば良いでしょう。 もしも、元の文字列を変えたい場合はgsub!メソッドを代わりに使えば良いだけです。 これをすべてのLaTeXソースファイルに対して行えば(これを簡単に行うためのツールが後述するLtxクラスです)、所期の目的を達成することができます。
もしも後者のように複数行にわたって記述する場合でも、若干複雑にはなりますが、スクリプトで変換可能です。 しかし、前者と後者の両方の書き方が混在していると、スクリプトはとんでもなく複雑になるでしょう。
結論としては、常に同じスタイルで記述することが大切です。 文書が大きくなればなるほど、このことは重要になってきます。
どのように複数のファイルを構成するか
個々のファイルの書き方も重要ですが、どのように複数のファイルを構成するかも重要です。 方法はいろいろあると思いますが、どの書籍を作る場合も同じ方法をとることが生産性を高めます。 私の場合は、次のような方針でファイルを構成しています。 あくまで一例とお考えください。 というのは、この2年間に作ったPDF書籍は、表紙、目次、本文という最小限の構成で作られていました。 ですので、索引や文献などを扱う方は、おそらくもう少しいろいろなことを考えなければならないと思います。
- ルートファイルとは、\documentclass、 \begin{document}、 \end{document}を記述したLaTeXのソースファイルのことを指し、ファイル名は「main.tex」とする
- サブファイルとは、ルートファイルが直接または間接に\inputコマンドで読み込むLaTeXソースファイルのことを指す
- プリアンブルの記述は「helper.tex」というサブファイルに記述する
- 表紙は「cover.tex」というサブファイルに記述する
- \chapterコマンドはルートファイルに記述し、\inputコマンドで、各章のサブファイルを読み込む
- \include、\includeonlyの2つのコマンドは使わない。
構成方法に正しい、正しくない、はありませんので、各人の好みのスタイルを構築すると良いと思います。 しかし、ここでは上記のように構成したとして、どのようなメリットがあるかを考えてみます。
執筆開始時点でまず章立てを決めます。 これは後に変更しても良いのですが、できればそうならないような、しっかりしたものが望ましいです。 すると、この時点でLaTeXのソースファイルはすべて決まってきますので、空のファイルを自動的に生成することが可能です。 生成のためには、章のタイトルとサブファイル名が必要です。 それをテキストファイルにしたものを、スケルトンと呼びます。 次にスケルトンのサンプルを示します。
# skeleton.txt
# skeleton.txt tells the outline of the book you want to make.
# string after # is ignored.
# empty line is ignored.
# format:
# "chaptername" subfilename
# Title above needs to be sandwiched between double quotes.
# subfilename will be added with .tex suffix.
"Chapter title" subfilename
"Next chapter" next_subfile
"Third chapter" third_subfile
さて、執筆開始時点からフォルダの初期化までを行うスクリプトがnewtexです。 これはbashのスクリプトです。 newtexは2回に分けて使います。 書籍のタイトルが「Sample Book」だとすると、まず、
$ newtex 'Sample Book'
とタイプします。 すると、「Sample_Book」というフォルダができます。 空白がアンダースコアに置き換えられていることに注意してください。 フォルダ内にskeleton.txtというファイルができるので、各章のタイトルとサブファイル名を書き直し、保存します。 そのフォルダでnewtexを引数なしで実行すると、ルートファイルとサブファイルを生成します。
このnewtexは、「LaTeX-BuildTools」という一連のbashスクリプトの一つです。
LaTeX-BuildTools
LaTeX-BuildToolsまたは、短くBuildToolsはLaTeXのソースファイルを生成したり、コンパイルするツールです。 詳しくはLaTeX-BuildToolsのReadme.mdをご覧ください。 このツールの特徴は
- サブファイルをテスト・コンパイル(※)できる
- latexmkを使ってルートファイルを必要回数だけコンパイルできる
- _buildディレクトリに補助ファイルとpdfファイルを生成するので、ソースディレクトリをきれいに保つことができる
- アーカイブファイルを生成できる
などです。 これとmakeを組み合わせることで、コンパイルの前処理(※)まで自動化することができます。
(※)テスト・コンパイルとここで呼んでいるのは、サブファイルだけをコンパイルすることです。 ファイルが大きくなるとルートファイルのコンパイルは時間がかかるようになります。 書籍作成途中でその出来具合をテストすることはたくさんありますが、その都度長時間のコンパイルはとても苦痛です。 それを軽減するのがこのテスト・コンパイルです。 仕組みとしては、サブファイルを取り込む仮のルートファイルを自動生成して、それをコンパイルします。 実質的にサブファイルのコンパイル時間しかかかりません。
(※)コンパイルの前処理が必要な場合があります。 例えば、gnuplotでグラフを描き、それをpngに落とす作業がそれにあたります。 そのpngファイルを\includegraphicsで取り込み、コンパイルします。 コンパイル前にgnuplotの処理が必要です。 これは、Makefileにその依存関係を書いておけば自動化できます。
このツールの最も使われるプログラムは「lb」というスクリプトです。 lbは「Latex Build」という意味で、LaTeXソースファイルをコンパイルするスクリプトです。
- 引数がルートファイルの場合は、latexmkを使ってルートファイルをコンパイル
- 引数がサブファイルのときは、ttexスクリプトを使ってサブファイルをテスト・コンパイル
latexエンジンはある程度自動的に判別してくれます。 詳細はgithubのReadme.mdをご覧ください。
Latex-SubsTools
Rubyは良くできたスクリプトなので、LaTeXソースファイルの一括置換などで力を発揮します。 そういうときは、ルートファイルとサブファイルの両方をいっぺんに置換したくなります。 そこで、すべてのファイルをバッファにとりこんだLtxというオブジェクトを使うと、これが難なくできます。
LtxクラスはLaTeX-SubsToolsの中のスクリプトです。 SubsToolsは、Subsutitute Toolsを圧縮したワードです。 SubsToolsはLtxクラスとそれを使った(一部は使っていませんが)Rubyスクリプトおよびシェル・スクリプトからなります。 その目的は主に一括置換です。 最初に示したeqnarray環境からalign環境への置換などがその例です。
Ltxクラスの最大の特長は、ルートファイルとサブファイルのすべてを回るdeep_eachメソッドです。 これを使うことにより、すべてのファイルをチェックして一括置換することができます。 例えば、次のスクリプトはセクション・コマンドをすべてチャプター・コマンドに置換します。 置換はルートファイルだけでなく、すべてのサブファイルに対しても行われます。
require 'ltx'
ltx = Ltx.new 'rootfile.tex'
ltx.backup
ltx.deep_each do |s|
s.gsub!(/\\section/,"\\chapter")
end
ltx.write
Rubyは強力な言語なので、単純な置換だけでなく、より複雑な操作をプログラムすることが可能です。
例えば、LaTeXソースファイルに、Rubyのソースファイルを含めたいとします。
\begin{verbatim}
..Rubyのソースファイル..
\end{verbatim}
という形で実現することになります。 もしも、Rubyのソースファイルを変更すると、そのたびにverbatim環境の中身を更新しなければなりません。 そこにinputコマンドが使えれば楽なのですが、あいにくverbatim環境のなかではコマンドは利きません。 そこで、insertcodeというRubyスクリプトを使います。
insertcodeは、次のような行があったときに、それをverbatim環境とRubyのソースファイル(「sample.rb」というファイルの内容)に置換します。
%insertcode{sample.rb}
これは一種の前処理なので、Makefileの中に依存関係とレシピを記述しておきます。 それによって、前処理が自動化されます。 insertcodeのソースファイル「insertcode.rb」の中でLtxクラスが使われていますので、Ltxの使い方の例としてご覧ください。
まとめ
書籍などの大きな文書を作るときには、当然ながらタイプの量が膨大になります。 ちょっと計算をしてみましょう。 ある人が1文字入力する時にエラーになる確率が0.01%であったとします。 極めて小さな値です。 逆にこの人が正しくタイプする確率は、99.99%です。 では、この人が10000字をタイプする時にすべて正しくタイプする確率はどうなるでしょうか。
(0.9999)^10000=0.36786104643297
約36.8%ということになります。 このことは何を示しているかというと、文書が大きくなればなるほで完全にタイプすることは難しいということです。 では、どうすれば良いのか?
有力な対応策は、できるだけコンピュータに仕事をさせ、人間の仕事を減らすことです。 例えば、
- ビルドの作業を自動化する(make, BuildToolsのnewtex, lbなど)
- 一括置換を使う(手作業の書き換えを極力減らす)
というようなことです。 もちろん、少量の作業であれば、コンピュータ化することはありません。 ヒューマンエラーの確率はそうは高くありませんから。 しかし、大きな文書では「作業のコンピュータ化」は効果的なだけでなく、必須であると言って良いと思います。 LatexToolsは「作業のコンピュータ化」によって書籍作成を支援するツールです。