複数人で1つのtexファイルを仕上げる(subfilesを使った統合)

# はじめに

以下のサイトを参考にしました．

* [分割した LaTeX ファイルを subfiles を使ってコンパイルする](https://qiita.com/sankichi92/items/1e113fcf6cc045eb64f7)
* [texの分割ファイルを統合して出力するスクリプトを書いたら論文執筆速度が爆速になった話](https://qiita.com/sakas1231/items/14c96f99d7507b928938)

ここに書く内容は，sakas1231様が書かれた後者の記事を大変参考にしています．

# 目的

「複数人で1つの報告書をtexで書きたい！」となりました．

要はtexを分割して書き，完成したものを統合したいとなりました．複数人で作業する以上，統合前に各自が書いたファイルをコンパイルし，不備などが無いか確認させたいです．

そこで*subfiles*を使ってファイルを分割することにしました．*subfiles*は分割されたファイル単体でコンパイルさせることができます．

しかし，親となるファイルで子の\citeや\refなどが読み込まれない問題を抱えています．

それを解決する方法として，親ファイルと子ファイルの内容を1つのファイルにまとめてしまえばいいという考え方があげられています([はじめに](#はじめに)の参考サイト2つ目参照)．

そこで

* 各自が担当箇所を書いてくる
* 統合するためのプログラムを実行する(私はpython3系で書きました)

といった手順を踏むことにしました．

# 構成

フォルダの中は以下のようになっています．

```text:
.
├── merge.py           ->texを1つに統合するためのプログラム
├── root.tex           -> 親となるtexファイル
└── subfiles           -> 統合するファイルが入ったフォルダの入れ場所
    ├── sub1           -> 統合するファイルが入ったフォルダ(共同著者各自が編集するのは以下)
    │   ├── sub1.bib   -> 統合するファイル
    │   └── sub1.tex   -> 参考文献のデータベースファイル
    └── sub2
        ├── sub2.bib
        └── sub2.tex
```

# 各ファイルの内容と説明

(merge.pyの説明だけで良い方は-> [merge.pyへ](#mergepy))

## root.tex

```tex:root.tex
\documentclass{jsarticle}
\usepackage{url}

\usepackage{subfiles}

\begin{document}

\section{root.tex}
この文章はroot.texに書かれています．

\subfile{subfiles/sub1/sub1}
\subfile{subfiles/sub2/sub2}

\end{document}
```

`\usepackage`は全てroot.texに書かれており，子ファイルには書き込みません．
サンプルでは，参考文献のurlを綺麗に表示するために`\usepackage{url}`をしています．
子ファイルの内容を書き込みたい位置で`\subfile{}`を使い，ファイルを指定してます．

## /subfiles/sub1/sub1.tex

```tex:sub1.tex
\documentclass[../../root]{subfiles}

\begin{document}

\section{sub1.tex}
この文章はsub1.texに書かれています．
bibtexが問題なく使えることを示すため，
Google\cite{google}を参考文献にあげます．

\bibliographystyle{junsrt}
\bibliography{sub1}

\end{document}
```

`\documentclass`で，親ファイルを指定します．後は普通のtexの書き方です．
もし，各自がダウンロードしたり，作ったりしたスタイルファイル(.sty)を使う場合は，`root.tex`と同じ階層に1つ，そのスタイルファイルを実際に使っている子ファイルのディレクトリに1つ必要になります．ご注意ください．

## subfiles/sub1/sub1.bib

```tex:sub1.bib
@misc{google,
	Howpublished = {\url{https://www.google.com}},
	Title = {Google}}
```

bibファイルの説明は省略

## subfiles/sub2/sub2.tex

```tex:sub2.tex
\documentclass[../../root]{subfiles}

\begin{document}

\section{sub2.tex}
この文章はsub2.texに書かれています．
複数の.bibファイルが問題読み込めることを示すため，
ここではYahoo!JAPAN\cite{yahooJP}を参考文献にあげます．

\bibliographystyle{junsrt}
\bibliography{sub2}

\end{document}
```

sub1.texと同じなので，説明省略

## subfiles/sub2/sub2.tex

```tex:sub2
@misc{yahooJP,
	Howpublished = {\url{https://www.yahoo.co.jp/}},
	Title = {Yahoo! JAPAN}}
```

bibファイルの説明は省略

## merge.py

```python:merge.py
import os
import sys
import pathlib
import re


class merger:

    # 引数として，root.texのフルパスとroot.texが含まれるディレクトリのパスが必要
    def __init__(self, root_tex: str, root_dir: str):
        self.root_tex = root_tex
        self.root_dir = root_dir

        # \usepackage{}など，\begin{document}より前に書かれる内容のリスト
        self.package = list()
        # \begin{document} から \end{document}までの内容
        self.lines = list()
        # 各子ファイルが含まれるフォルダに存在する.bibファイルの絶対パス
        self.biblist = list()

    # メイン部分
    def start(self):
        # root.texを読んでいく
        with open(root_tex, mode='r') as root_tex_f:

            # \begin{document}以下の内容かどうか
            indoc_flag = False

            for line in root_tex_f:
                # コメント文はスキップ
                if line.strip().startswith('%'):
                    pass
                
                # subfileを読み込んでいる場合
                elif line.strip().startswith('\\subfile'):
                    self.subfile(line)
                
                else:
                    # \begin{document}がきたら
                    if line.strip() == '\\begin{document}':
                        indoc_flag = True
                    # \end{document}がきたら
                    elif line.strip() == '\\end{document}':
                        indoc_flag = False
                    # \begin{document}~\end{document}の外
                    elif indoc_flag:
                        self.lines.append(line)
                    # \begin{document}~\end{document}の中
                    elif not indoc_flag:
                        self.package.append(line)

        # 統合後のファイルmerge.texを作成する．既に存在する場合は作らない
        try:
            with open(root_dir + '/merged.tex', mode='x') as merged_tex_f:
                merged_tex_f.write(''.join(self.package))
                merged_tex_f.write('\n')
                merged_tex_f.write('\\begin{document}\n')
                merged_tex_f.write(''.join(self.lines))
                merged_tex_f.write('\\end{document}\n')
                merged_tex_f.write('\n')
                merged_tex_f.write('\\bibliographystyle{junsrt}\n')
                # bibファイルはカンマ区切りで複数指定できるので，サブファイルに使われていたもの全てを指定する
                merged_tex_f.write('\\bibliography{0}{1}{2}'.format('{', ','.join(self.biblist), '}'))
                merged_tex_f.write('\n')
                merged_tex_f.write('\\end{document}')
        except FileExistsError:
            print('>> merged.tex already exists. Please delete it first.')

        print('Finish!!!')

    # root.texの\subfile{}を処理する部分
    def subfile(self, line: str):
        # \subfile{hogehoge}を受け取り，そこからsubfileのパスを生成する．
        # ディレクトリの構造が先にあげたようになっていないと正しく動かない
        subfile_tex = '{0}/{1}.tex'.format(self.root_dir, re.split('[{}]', line)[1])

        # ファイルがない場合
        if not os.path.isfile(subfile_tex):
            return

        # サブファイルのディレクトリのパス
        subfile_dir = os.path.dirname(subfile_tex)

        # \begin{docunemt}~\end{document}の中か
        is_inDocument: bool = False

        with open(subfile_tex, 'r') as subfile_tex_f:
            for line in subfile_tex_f:
                # コメント無視
                if line.strip().startswith('%'):
                    pass
                else:
                    # \begin{document}~\end{document}内
                    if is_inDocument:
                        # \end{}がきたら
                        if line.strip() == '\\end{document}':
                            is_inDocument = False
                        # bibtexの設定に関する部分もスキップ(親でやるので)
                        elif line.strip().startswith('\\bib'):
                            pass
                        # それ以外は全てコピー
                        else:
                            self.lines.append(line)
                    
                    # \begin{}~\end{}の外
                    else:
                        # \usepackageで親で指定していないものがきた場合は追加
                        if line.strip().startswith('\\usepackage'):
                            if not line.strip() in self.package:
                                self.package.append(line)
                        # /begin{}が来るまで待機
                        elif line.strip() == '\\begin{document}':
                            is_inDocument = True

        # subfileのディレクトリにbibファイルがある場合は，その絶対パスを取得
        for bib in os.listdir(subfile_dir):
            if os.path.splitext(bib)[1] == '.bib':
                self.biblist.append('{0}/{1}'.format(subfile_dir, bib))


if __name__ == '__main__':

    # 標準入力でroot.texの絶対パスをもらう
    root_tex = input('Enter absolute path for root.tex\n>> ').strip()
    
    # ファイルが存在しない場合
    if not os.path.isfile(root_tex):
        print('>> Not exist file.')
        exit(0)

    # 拡張子が.texではない場合
    if not os.path.splitext(root_tex)[1] == '.tex':
        print('>> Not tex file.')
        exit(0)

    # 絶対パスではなく，相対パスが入力された時
    if not pathlib.Path(root_tex).is_absolute():
        print('>> Not absolute path')
        exit(0)

    # root.texの絶対パスから，親フォルダのパスだけ取得
    root_dir = os.path.dirname(root_tex)

    # root.texの絶対パスと親フォルダのパスを引数に初期化
    merger = merger(root_tex, root_dir)

    # 開始
    merger.start()
```

コメントを細かめに書いたので，ここでの説明は省略します．
無駄があったり綺麗じゃなかったりするかもしれませんがお許しください．

# 使い方・結果

1. 子となるtexを書き，指定のディレクトリ構造にしたがってsubfiles下に置きます．
2. `merge.py`を実行します．(`python3 merge.py`)
3. root.texのパスを標準入力で入力します．

    ```
    Enter absolute path for root.tex
    >> /Users/********/Desktop/sample/root.tex 
    Finish!!!
    ```

4. merge.texがroot.texと同じ階層に書き出されます．


    ```tex:merge.tex
    \documentclass{jsarticle}
    \usepackage{url}
    \usepackage{subfiles}
    
    
    \begin{document}
    \section{root.tex}
    この文章はroot.texに書かれています．
    
    \section{sub1.tex}
    この文章はsub1.texに書かれています．
    bibtexが問題なく使えることを示すため，
    Google\cite{google}を参考文献にあげます．
    
    
    
    \section{sub2.tex}
    この文章はsub2.texに書かれています．
    複数の.bibファイルが問題読み込めることを示すため，
    ここではYahoo!JAPAN\cite{yahooJP}を参考文献にあげます．
    
    
    
    \end{document}
    
    \bibliographystyle{junsrt}
    \bibliography{/Users/********/Desktop/sample/subfiles/sub1/sub1.bib,/Users/********/Desktop/sample/subfiles/sub2/sub2.bib}
    \end{document}
    ```

5. merge.texをコンパイルして完成です．


# 感想

あくまで複数人で1つのtexファイルを仕上げることを目的としているため，subfilesで指定するファイルの階層が深かったり，各子ファイルごとにスタイルファイルがいるなどの制約がありますが，個人で書くtexをでsubfilesで分割したいだけであれば，もっと簡単になるはずです．

まぁ，subfilesがbibtexまで対応すれば完璧なんですけどね...(ナイモノネダリ)