かすみん日記

暇なときに何か喋ります

【LaTeX】ファイルを分割してサブディレクトリに配置する

本記事の目的

大規模なtex文書をファイル分割することでQOL向上に努めた証の記録。

順を追って解説します。

環境・バージョン

タイプセットはlatexmk (uplatex + dvipdfmx)コマンドで行います。

最後の方では、vscodeというエディタの拡張機能LaTeX Workshopを用いてタイプセットする方法も書いています。

素朴な方法

分割したtexファイルをsection01.texsection02.texとします。

これらをmaster.texで読み込んで、pdfファイルを作ります。

それぞれのファイルの中身は以下のような感じです。

%% master.tex
\documentclass[uplatex,dvipdfmx]{jsarticle}
\begin{document}
  \input{section01.tex}
  \input{section02.tex}
\end{document}
%% section01.tex
\section{ひとつめ}
ほげほげ
%% section02.tex
\section{ふたつめ}
ほげほげ2

分割したファイルsection01.texsection02.texには、document環境の中身だけを書きます。

master.texファイル内で\inputコマンドを書いたところに、引数で与えたファイルの内容がそのまま挿入されます。

つまり、master.texファイルをタイプセットすれば、全ての内容を含んだ文書が完成するわけです。

しかし、この方法では、分割したファイルを単体でタイプセットすることができません。

分割したファイルにはdocument環境の中身しか書いていないので、tex文書としては不完全であるからです。

この不満点は、docmuteパッケージを用いることで解決できます。

docmuteパッケージ

docmuteパッケージを用いると、\inputで分割ファイルを挿入する際に、document環境内のみを抽出して読み込んでくれます。 (正確には、document環境を改造して、入れ子になったdocument環境やプリアンブルを無視するようにしている。)

例えば、次のようにして使います。

%% master.tex
\documentclass[uplatex,dvipdfmx]{jsarticle}
\usepackage{docmute}
\begin{document}
  \input{section01.tex}
  \input{section02.tex}
\end{document}
%% section01.tex
\documentclass[uplatex,dvipdfmx]{jsarticle}
\usepackage{docmute}
\begin{document}
  \section{ひとつめ}
  ほげほげ
\end{document}
%% section02.tex
\documentclass[uplatex,dvipdfmx]{jsarticle}
\usepackage{docmute}
\begin{document}
  \section{ふたつめ}
  ほげほげ2
\end{document}

このとき、分割したtexファイルは、単体でもタイプセットを行うことができます。

さらに、master.texもそのままタイプセットができます。

分割ファイルで、目次や文献の出力を行う

せっかく、分割したファイルを単体でタイプセットできるようになったので、その場で目次や文献の出力の確認も行いたいところです。

いくつか方法は思いつきますが、例えば、シンプルにif文を使って、今タイプセットしているファイルが分割ファイルかmaster.texなのか判定して処理しましょう。

プリアンブルは各ファイルで共有したいので、mymacro.styファイルを作って読み込むことにします。

%% ディレクトリ構造
/
|- mymacro.sty
|- mybib.bib
|- master.tex
|- section01.tex
`- section02.tex
%% mymacro.sty
\newif\ifsubfile

\newcommand\mokuji{
  \ifsubfile
    \tableofcontents
    \clearpage
  \else
    \relax
  \fi
}

\newcommand\bunken{
  \ifsubfile
    \clearpage
    \bibliographystyle{junsrt}
    \bibliography{mybib}
  \else
    \relax
  \fi
}
%% mybib.bib
@book{hoge2022,
  title = {ほげほげ},
  author = {ほげ星人},
  year = {2022}
}
%% master.tex
\documentclass[uplatex,dvipdfmx]{jsarticle}

\usepackage{docmute}
\usepackage{mymacro}

\begin{document}
  %% 目次
  \tableofcontents

  %% 本文
  \input{section01.tex}
  \input{section02.tex}

  %% 文献
  \bibliographystyle{junsrt}
  \bibliography{mybib}
\end{document}
%% section01.tex
\documentclass[uplatex,dvipdfmx]{jsarticle}

\usepackage{mymacro}
\subfiletrue

\begin{document}
  %% 目次
  \mokuji

  \section{ひとつめ}
  ほげほげ\cite{hoge2022}%% 文献
  \bunken
\end{document}

分割したtexファイルでは\subfiletrueと書いてあるので、\ifsubfiletrueになります。

master.texをタイプセットするときには、それはfalseなので、\mokuji\bunkenは何もしないコマンド(\relax)になります。

分割ファイルをサブディレクトリに入れる

分割したファイルが多くなってくると、サブディレクトリに格納したくなります。

texファイルなどをサブディレクトリに配置すると、パスの設定をちゃんとしないといけなくなります。

ここでは、以下のようなディレクトリ構造にしてみます:

/
|- .vscode/settings.json
|- fonts/
|    `- フォントファイル
|- sty/
|    `- mymacro.sty
|- sections/
|    |- 01.tex
|    `- 02.tex
|- latexmkrc
|- master.tex
`- mybib.bib

まず、latexmkの設定ファイルであるlatexmkrcファイルには、以下のように書きます:

#!/usr/bin/env perl

## latexコマンドの共通オプション
$latex_options = '-synctex=1 -halt-on-error -file-line-error -interaction=nonstopmode';

if ($^O eq 'MSWin32') { # Windows の場合
  $latex   = "uplatex %O $latex_options -kanji=utf8 -no-guess-input-enc %P";
} else { # Windows以外のOSの場合(Linux, macOS)
  $latex   = "uplatex %O $latex_options %P";
}
$lualatex  = "lualatex %O $latex_options %S";
$biber     = "biber %O --bblencoding=utf8 -u -U --output_safechars %B";
$bibtex    = "pbibtex %O %B";
$makeindex = "upmendex %O -o %D %S";
$dvipdf    = "dvipdfmx %O -o %D %S";

## latexコマンドの最大実行回数
$max_repeat = 5;

## $dvipdfを利用して.dviからPDFを作成
$pdf_mode = 3;

## specifies cleanup mode
## dvi, ps, pdfファイル以外を削除
$cleanup_mode = 2;

## -c optionで消すファイルの拡張子
$clean_ext = "bbl dvi";

## show CPU time used
$show_time = 1;

## パスの設定 - - - - - - - - - - - - - - - - - - - - -

## ルートディレクトリを探して登録
$key_file = 'master.tex';
if (-e $key_file) {
  $root_dir = '.';
} elsif (-e '../' . $key_file) {
  $root_dir = '..';
}

## styやfontを探索するパスを追加
if ($root_dir){
  $ENV{'TEXINPUTS'}     = $root_dir . '/;' . $root_dir . '/sty//;';
  $ENV{'OPENTYPEFONTS'} = $root_dir . '/fonts//;';
}

前半部分は好きに書いて下さい。後半部分でパスの設定をしています。

分割したtexファイルの配置場所を変えたので、master.texの内容も少し修正します:

%% 修正前
\input{section01.tex}
\input{section02.tex}

%% 修正後
\input{sections/01.tex}
\input{sections/02.tex}

ここで、sections/01.texには、上のsection01.texの内容をそのままコピペして下さい。02の方も同じです。

サブディレクトリに配置したtexファイルから見て、bibファイルの相対位置が変わったので、\bunkenマクロを少し修正します。

\newcommand\bunken{
  \ifsubfile
    \clearpage
    \bibliographystyle{junsrt}
%%  \bibliography{mybib}    % 修正前
    \bibliography{../mybib} % 修正後
  \else
    \relax
  \fi
}

他のファイルは、上で作ったそのままで良いです。

master.texをタイプセットするには、それと同じディレクトリに移動して

latexmk master.tex

と打ちます。

分割したセクションのtexファイルをタイプセットするには、sectionsディレクトリに移動してから

latexmk -r ../latexmkrc 01.tex

と打ちます。設定ファイルをオプションで直接指定しています(面倒ですが)。

vscodeでタイプセットするには、.vscode/settings.jsonに以下の設定を書きます:

{
  "latex-workshop.latex.tools": [
    {
      "name": "Latexmk for sub dir",
      "command": "latexmk",
      "args": [
        "-e",
        "read_first_rc_file_in_list( '../latexmkrc' );",
        "%DOC%"
      ]
    },
  ],
  "latex-workshop.latex.recipes": [
    {
      "name": "MASTER",
      "tools": [
        "Latexmk for sub dir"
      ]
    },
  ]
}

↑を書いたら、vscodeを再起動します。(作業ディレクトリを開き直す)

そうしたら、master.texsections/01.texなどを開いて、サイドバーの「TeX」から「Build LaTeX project」の「Recipe: MASTER」をクリックすれば、タイプセットができるはずです。

また、サブディレクトリのsections/内のtexファイルで、\includegraphicsコマンドなどを使う場合には、挿入する画像や図のファイルのパスは、master.texファイルから見た相対パスを書けばよいです。

以上、説明が冗長な割にクソわかりにくいかと思いますが、とりあえず忘れないうちに記録しておきました。