TikZ を standalone でサブファイル化したい
TikZ は LaTeX 上で描画する強力なツールです。一方で、TikZ コードが本文ファイル内にある場合、以下のような問題が生じます。
- TikZ・本文のいずれか一方を作成中に、他方もタイプセットに含まれるため、試験的なタイプセットが長くなってしまう
- TikZ コードと本文がファイル内に混在するため、ファイル全体が煩雑になる
これらの解決方法は簡単で、TikZ コード(tikzpicuture 環境)のみを抜き出し、別ファイルとして作成し、必要な段階で TikZ イメージを挿入できるようにしてしまえば良いのです。(サブファイル化)しかし、単にサブファイル化して \input しても、サブファイルは単独でタイプセットすることが出来ません。
このような TikZ 単体でタイプセットし、メインファイルに後から挿入する方法を実現するには、standalone パッケージが便利です。
本記事では TeX ソースのまま挿入する方法を考えます。
実は、standalone によってサブファイル化された TikZ イメージの挿入は、PDF を \includegraphics する方法もあります。しかし、TeX ソースをそのまま挿入する方法は、記事としてあまり見かけることはありませんでした。本記事はそんなスキマを埋めるスキマ記事です。
■ 前提
本記事では、メインファイルと TikZ ソースのみのファイルを、以下のようなディレクトリ構造で作成していると想定します。また、tikz フォルダにある tikz1.tex や tikz2.tex は独立してタイプセットでき、これらで描画された TikZ イメージを main.tex に挿入します。
.
├─ main.tex
└─ tikz/
├─ tikz1.tex
├─ tikz2.tex
├─ ...
└─ tikzN.tex
TikZ コード部分を抜いた本文のあるファイルを “メインファイル” (main.tex)、TikZ を描画するためだけのファイルを “サブファイル” (tikz1.tex, tikz2.tex...) と呼ぶこととします。
■ standalone のきほん
standalone には文書クラスとパッケージのそれぞれにオプションがあります。また、提供されるコマンドにもコマンドオプションがあります。
記事内では、
- 文書クラスオプション
- パッケージオプション
- コマンドオプション
と区別して呼称しますが、この違いに注意してください。
standalone はパッケージと文書クラスの両方があるバンドルです。メインファイルでパッケージを \usepackage、サブファイルで文書クラスを \documentclass します。
standalone パッケージのデフォルトでは、サブファイルのプリアンブル(以下、サブプリアンブル)は、メインファイルで読みません。
多くの場合、サブファイルにもプリアンブルが存在するため、そのままではメインファイル内でサブファイルを含んでタイプセットすることは出来ません。
メインファイルに standalone 文書クラスで作成したサブファイルのプリアンブルを含めるには、subpreambles パッケージオプションを有効にする 必要があります。このオプションを有効にすると、サブプリアンブルが .sta (standalone) ファイルに転記され、これが 2 回目以降の実行時に読み込まれます。
standalone を利用したサブファイルを含めたメインファイルのタイプセットには少なくとも 2 回の実行が必要になります。また、挿入するサブファイルが最大 N 回のタイプセットを必要とする場合、メインファイルのタイプセットは全体で N+1 回のタイプセットが必要になります。これは、1 回目のタイプセットは standalone パッケージがサブプリアンブルを読む処理に利用されるためです。このとき、サブファイルの TikZ コードは一切読んでいないようです。
■ サブファイル
サブファイルでは、standalone を文書クラスとして読み込みます。今回は、TikZ を作成することのみを考えているため、tikz 文書クラスオプションを有効にします。これによって、tikzpicture 環境内のみが 1 つをコンテンツとした PDF が作成されるようになります。
standalone はサブファイルのプリアンブルをメインファイルに持ち込むことが出来ますが、メインファイルでも同じパッケージを \usepackage すると良いです。(特に pgfplots パッケージが想定されます)
これは非自明なパッケージの衝突を、非意図的に生じさせることを避けるためです。もちろん、サブファイルだけで読み込んだパッケージによる衝突を解消する方法はありますが、煩雑になる可能性があります。
これを踏まえると、サブファイルは次のように書かれます。
% tikz1.tex, tikz2.tex
\documentclass[tikz]{standalone}
\tikzlibrary{
% TikZ で必要なライブラリ
}
\tikzset{
% TikZ に関する設定
}
\begin{document}
\begin{tikzpicture}
% ここに TikZ コードを記述する
\end{tikzpicture}
\end{document}
もしも、サブファイルで共通するプリアンブルを利用したい場合は、サブプリアンブル用の別ファイルを作成し、プリアンブル内で \input すれば良いでしょう。
このサブファイルには、フォント関連のプリアンブルを含めると危険な場合があります。これは、メインファイルへ TeX 形式で挿入した場合、メインファイルのフォントも変更してしまうためです。
もしも、サブファイルではメインファイルと異なるフォントを利用したい場合は、Image 形式で挿入することをオススメします。
ちなみに、ここで利用される文書クラスは article です。TikZ のみを利用する場合には、文書クラスで違いが出ることは無いでしょう。もしも変更したい場合は、class 文書クラスオプションを利用します。(e.g. class=jlreq)
■ メインファイル
メインファイルでは、subpreambles パッケージオプションを有効にした standalone パッケージを早い段階で導入します。また、ここでは詳細にしませんが、sort パッケージオプション(サブプリアンブルの適当な並び替え)も有効にしておくと便利です。
メインファイルには \usepackage{tikz} が必要です。
サブファイルで tikz 文書クラスオプションを利用していても、メインファイルに \usepackage{tikz} は読み込まれません。
TikZ イメージを含めたい箇所で \includestandalone を書けば挿入されます。ファイルの拡張子は、extension オプションによって指定されるため、\includestandalone で明記する必要はありません。
% main.tex
\documentclass{jlreq}
\usepackage[subpreambles, sort]{standalone}
% include other packages
% Example: amsmath, mathtools, hyperref, graphicx, float ...etc.
\usepackage{tikz}
\begin{document}
% Main text...
% \begin{figure}[tbp]
% \centering
\includestandalone{tikz/tikz1}
% \caption{Drawing image by TikZ}
% \label{fig:tikz-image1}
% \end{figure}
% Main text...
% \begin{figure}[tbp]
% \centering
\includestandalone{tikz/tikz2}
% \caption{Drawing image by TikZ}
% \label{fig:tikz-image2}
% \end{figure}
% Main text...
\end{document}
文書クラスオプションはオプションを有効にする順序を気にします。そのため、前よりも後のオプションが優先されます。
▽ プリアンブルの衝突と回避
複数のサブファイルとメインファイルを統合する場合、プリアンブルで衝突が生じることがあります。
subpreambles, sort パッケージオプションでも、サブプリアンブルで衝突が生じる場合、print パッケージオプションを与えて、別ファイルにすべてのサブプリアンブルを書き出して、手動で回避する方法を考えましょう。
\usepackage[subpreambles, print, sort]{standalone}
これによって、サブファイルを挿入せずにタイプセットが終了し、.stp (standalone, print) ファイルが生成されます。このファイルには、すべてのサブプリアンブルが転記されています。この後、次の手順に従います。
- このファイルを編集して、プリアンブルが衝突しない順序に書き変える
- メインファイルに書き変えたプリアンブルを追加する
-
printパッケージオプションを無効化する - メインファイルをタイプセット
もしも、コメントアウトされた部分も .stp ファイルに転記したい場合は、comments パッケージオプションを有効にします。
▽ 挿入する TikZ イメージの調整
挿入する TikZ イメージの大きさ等をコマンドオプションから調整することが出来ます。
\includestandalone は graphicx パッケージの \includegraphics に類似したコマンドオプションを受け付けます。これは TeX 形式での挿入でも機能します。
頻繁に利用されるオプションは、次のようなものがあります。
| コマンドオプション | 説明 |
|---|---|
width・height
|
横幅・縦幅の指定 |
scale |
スケール調整 |
angle |
回転角度の指定 |
draft |
ドラフトモードへの切り替え |
page |
ページ指定(Image 形式のみ) |
\includegraphics でも頻繁に利用しますが、width を \textwidth・\linewidth の比で指定すると簡便です。
\includestandalone[ width=.8\textwidth ]{tikz/tikz2}
▽ サブファイルの挿入形式
これまで、サブファイルの挿入形式はソースコードとして挿入することを考えてきました。しかし、実は Image 形式として挿入する方法もあります。ここでは、これら 2 つの挿入形式の指定方法を紹介します。
サブファイルを挿入する \includestandalone は以下の 2 つの形式を利用することが出来ます。
- TeX 形式(デフォルト)
- Image 形式(PDF あるいは EPS ファイル)
-
extensionオプションで拡張子を指定 - デフォルトでは、コンパイラのターゲット出力ファイルの拡張子によって規定されている
- pdflatex、lualatex、xelatex:
.pdf - latex、(u)platex:
.eps
- pdflatex、lualatex、xelatex:
-
TeX 形式での挿入は standalone が行う最も基本的な動作であり、本記事の紹介したかった挿入方法です。しかし、この方法は必ずしも Image 形式での挿入よりもメリットがあるということではありません。
Image 形式でメリットとなる点は、以下の 2 点があります。
- プリアンブルの衝突が生じない
- メインファイルと異なるフォントを簡単に利用できる
特にプリアンブルの衝突は大きなストレスになるため、Image 形式を利用する最大の理由になります。
これらの形式を選択するには、mode によるパッケージ・コマンドオプションから指定が必要です。mode の採りうる値は以下の 3 つと build 系の 3 つの計 6 つがあります。
mode の値 |
挿入形式 |
|---|---|
tex (default) |
TeX |
image |
Image |
image|tex |
Image が無い場合、TeX が指定される |
\includestandalone[mode=image] は \includegraphics と等価なものになります。mode=image を選ぶのであれば、\includegraphics を利用した方がこれまでの使用感で利用でき、精神的にも楽かもしれません。
一方で、mode=image にはデメリットもあります。
- Image 形式が作成されていないと、メインファイルのタイプセット時にコケる
- サブファイルを更新した場合、Image 形式も更新する必要がある
これらを解決するために、サブファイルの Image 形式をメインファイルのタイプセット時に生成する mode があります。(本記事では、これらを “build 系” と表現します)
mode の値 |
サブファイルのタイプセット条件 |
|---|---|
build |
常にタイプセットする |
buildmissing |
Image 形式が無いとき |
buildnew |
サブファイルが更新されたとき |
これらの build 系を指定した場合、-shell-escape が必要になります。
もしも -shell-escape 無しで実行した場合、デフォルトの tex が指定されます。また、タイプセット方法はメインファイルと同じタイプセット方法を採ります。
build 系は、サブファイルを更新した場合であっても常に新しい Image 形式を取得することが出来ます。image では手動で更新しない限り最新のサブファイルを Image 形式として取得できません。
ただし、buildnew・buildmissing は少し曲者です。どちらも、サブファイルのタイプセット条件を満たさないときにはサブファイルをタイプセットしないので、多くとも 1 回しかタイプセットしません。
そのため、複数回のタイプセットが必要な場合は、期待した結果を得られない可能性があります。
■ (u)pLaTeX 利用時の注意
サブファイルでは、文書クラスで dvipdfmx の指定が必要になります。
\documentclass[dvipdfmx, tikz]{standalone}
もしも、tikz 文書クラスオプションが上手く機能しなかった場合、次のようにしてください。これは、tikz 文書クラスオプションと等価なものとなっています。
\documentclass[dvipdfmx, varwidth=false, multi=tikzpicture]{standalone}
\usepackage{tikz}
また、(u)pLaTeX では、サブファイルに複数コンテンツ(複数の tikzpicture 環境)がある場合、コンテンツを上手く切り出せない問題が生じます。そのため、1 つのファイルに 1 つのコンテンツを徹底する必要があります。
メインファイル内で mode=image の下で \includestandalone とする場合、(u)pLaTeX のデフォルトでは EPS ファイルが対象になります。
そのため、extension=.pdf を指定する必要があります。このとき、extension オプションの値は . から始まることに注意してください。
\usepackage[mode=image, extension=.pdf]{standalone}
\includestandalone[mode=image, extension=.pdf]{tikz/tikz1}
もちろん、サブファイルの PDF から EPS を作成すれば extension=.pdf を指定しなくても良いです。しかし多くの場合、サブファイルは PDF で出力されていることでしょう。また、Image 形式であれば \includegraphics を利用する方が簡便かもしれません。
ちなみに、build 系を利用すると dvips を勝手に実行して EPS を作成してくれます。そのため手動では、サブファイルを TeX →(LaTeX)→ DVI →(dvips)→ EPS とするべき操作がすべて不要になります。これであれば、(u)pLaTeX でも EPS ファイルを簡単に挿入することが出来ます。(ただし、現在では EPS ファイルの挿入は避けるべきとされています)
■ テストファイル
以上の使い方を具体的に示してみたい。
メインファイルと合わせて、これらを以下のようなディレクトリに配置します。
.
├─ main.tex
└─ tikz/
├─ flowchart.tex
└─ skip-connection.tex
% main.tex
\documentclass{jlreq}
\usepackage[left=25mm,right=25mm,top=30mm,bottom=30mm]{geometry}
\usepackage[
subpreambles, sort
% ,mode=build
]{standalone}
\usepackage{hyperref}
\usepackage{caption}
\usepackage{tikz}
\begin{document}
Hellow \LaTeX{}!!
\section{Flowchart}
\begin{center}
\includestandalone[width=.8\textwidth]{tikz/flowchart}
\captionof{figure}{\nolinkurl{https://tikz.net/flowchart/}}
\end{center}
\section{Skip Connection}
\begin{center}
\includestandalone[width=.8\textwidth]{tikz/skip-connection}
\captionof{figure}{\nolinkurl{https://tikz.net/skip-connection/}}
\end{center}
\end{document}
サブファイルで作成する TikZ はそれぞれ tikz.net から適当に引用します。
今回、メインファイルのタイプセットは LuaLaTeX を用います。また、複数回のタイプセットが必要なため、latexmk (ver.4.80) を利用します。
$ latexmk -lualatex (-shell-escape) main.tex
mode=build 下では -shell-escape が必要なため、適宜有効にします。
Q. 挿入形式が TeX と Image では、メインファイルの成果物 (PDF) のファイルサイズはどのくらい変わるのか。
| 挿入形式 | ファイルサイズ (byte) |
|---|---|
| TeX | 52,851 |
| Image ( mode=build) |
69,901 |
Image 形式を利用して挿入した場合、TeX を利用して挿入した場合に比べて、少し大きくなりました。これは、挿入した PDF のヘッダー情報が含まれるためだとされています。
したがって、PDF にメインファイルに無いフォントが含まれる場合、より大きなファイルになる可能性があります。
Q. 挿入形式が TeX と mode=build では、メインファイルの生成にかかる時間はどのくらい変わるのか。
実行・計測は以下のようにします。
$ powershell -C Measure-Command {latexmk -lualatex (-shell-escape) main.tex}
| 挿入形式 | 秒 |
|---|---|
| TeX | 23.8017767 |
| Image ( mode=build) |
52.224716 |
mode=build では、すべてのサブファイルを個々の PDF として生成する必要があるため、mode=tex よりも時間がかかってしまいます。今回は、倍程度の差が生じました。
具体的に mode=build はメインファイル 1 回のタイプセットで次の順でファイルを実行します。
- メインファイル
- サブファイル 1
- サブファイル 2
- ...
- サブファイル N
- メインファイル
ここで、メインファイルにはサブファイルが Image 形式で挿入されています。しかし、サブファイルは複数のタイプセットが必要になる場合があります。(※ この時点では、サブファイルは必要回数のタイプセットを実行されていません)
完成品としての最終タイプセットであれば、どれだけ長いタイプセットでも待てば良いだけなので、必ずしも build 系が悪いものにはならないでしょう。
余談
Q. standalone パッケージって、LaTeX で生成される数式とかを余白の無い PDF で出力するためのパッケージじゃないの??
開発されたモチベーションは違います。(と言いつつ、convert オプションで PNG 出力までアプローチされていますが)
この記事で書いたような、「特定の部分を外部ファイルにして取り出し・挿入したい」がモチベーションになります。そのため、いくつかの競合パッケージが存在します。この点は次節で。
サブファイルを TeX 形式で挿入する場合、中間ファイルに .sta ファイルが作成されます。そのため、メインファイルのタイプセット中にエラーが生じた場合、.sta ファイルも削除しましょう。latexmk や llmk などのビルドツールや LaTeX Workshop (VSCode) が対象とする削除ファイルには含まれていません。ここには注意が必要です。
サブファイル化された TikZ を Image 形式として挿入している人は、graphicx パッケージを利用して挿入する方が多勢になっているかもしれませんが、実は standalone パッケージからでも挿入することが出来ます。また、build 系を利用すれば、サブファイル更新時に挿入される Image 形式が最新のものかどうか深く注意する必要がなくなります。
ただ、どこかに寄稿するなどで TeX ファイルを渡して他人にタイプセットを任せる場合には、サブファイルを Image 形式 (mode=image) にしておくべきでしょう。余計な問題を回避できる可能性があります。その人が -shell-escape してくれるかも分かりません。
他の方法との比較
§ 4.3 Similar packages and classes - The standalone Package
standalone の他にも類似パッケージが存在しています。standalone のガイドには、他の類似パッケージとの差別化点が記載されています。
doumute・subfiles パッケージは、サブプリアンブルをメインファイルのプリアンブルに取り込むような機能がありません。standalone はこの点で優れています。しかしながら、非意図的なパッケージの衝突がメインファイルのタイプセット中に発生する可能性があります。
external ライブラリは、メインファイルにある TikZ コードを一時的に TikZ イメージだけの出力に変更するためのライブラリです。メインファイルが煩雑で散らかった状態であることに変わりはなく、その設定方法も少し難解なもののように思われます。(参照)