Posted at

メモ: Pandocのテンプレートとデータディレクトリの優先順位

More than 1 year has passed since last update.

Pandocのテンプレート・ユーザデータディレクトリに関して質問があったのでメモしておきます。


何の話か

Pandocの出力をカスタマイズする仕組みとして、テンプレートまたはユーザデータがあります。

詳細は User's GuideのTemplates を参照。


テンプレート

例えばHTMLやLaTeXで出力する際に、「ヘッダ・フッタをカスタマイズしたい」「スタイルファイルへのリンクを挿入したい」「変数で条件分岐させたい」というケースに、ユーザはテンプレートを編集できます。


ユーザデータ

テンプレートが使えない例外がたまにあり、例えばWord docx(docx)やLibre office Writer(odt)の書式カスタマイズはこちらでやります。

詳細は ドキュメント変換ツールPandoc:ユーザーズガイドを熟読して分かったマニアックな使い方 - Qiita をご覧ください。


ユーザデータディレクトリ

テンプレートとユーザデータを格納するディレクトリをデータディレクトリと呼び、そのうちユーザがカスタマイズするためのものをユーザデータディレクトリと呼びます。

データディレクトリのうち、特に「templates」以下のファイルを テンプレート と呼び、それ以外を データファイル と呼びます。

ただしテンプレートは



  • --template オプションで指定する場合

  • ユーザデータディレクトリを指定して格納する場合

があります。後者では、ユーザデータディレクトリの中の「templates」ディレクトリにテンプレートを入れます。 (これを忘れると読まれません)

ユーザデータディレクトリには --data-dir オプションで直接指定できるものと、Pandocが(既定のデータディレクトリよりも優先して)勝手に読みにいくデフォルトユーザデータディレクトリがあります。

デフォルトユーザデータディレクトリの場所は通常、 pandoc -v で示されます。 (Default user data directory: hoge という形で)

実際には


  • Windows 7以降: %HOME%\AppData\Roaming\pandoc

  • Unix系 (macOS/Linux): ~/.pandoc

であることが多いです。

ただし、勝手に作られるとは限らないので、そのディレクトリがない場合は自分でディレクトリを作ります。

以下、Pandocのテンプレートとユーザデータディレクトリについて、どのように適用されるのか(適用の優先順位)について説明します。


既定のデータディレクトリ

Pandocが既定で使うデータディレクトリが実際のマシン上でどこに置かれるかについては、ドキュメント上の明示はありません。ただし、GitHubディレクトリの

https://github.com/jgm/pandoc/tree/master/data

において、確認することができます。

ただし、公式で推奨する方法は、


  • テンプレート: pandoc -D FORMAT または pandoc --print-default-template FORMAT (FORMATは -f/-t で指定するフォーマット名)

  • ユーザデータ: pandoc --print-default-data-file FILE (FILEはデータファイルの名前)

によって出力することです。(標準出力に出るので、リダイレクトで保存します)


テンプレート・ユーザデータディレクトリの優先順位

どの優先順位でテンプレートやユーザデータディレクトリが使われるかは、(よく読めば)Pandocのユーザーズガイドに書かれています。

結論から言えば

テンプレートを引数で指定 (--template) > ユーザデータディレクトリを引数で指定 (--data-dir) > デフォルトユーザデータディレクトリ > Pandoc既定のデータディレクトリ

のようです。

順に見ていきます。


--template=FILE

Use FILE as a custom template for the generated document. Implies --standalone. See Templates, below, for a description of template syntax. If no extension is specified, an extension corresponding to the writer will be added, so that --template=special looks for special.html for HTML output. If the template is not found, pandoc will search for it in the templates subdirectory of the user data directory (see --data-dir). If this option is not used, a default template appropriate for the output format will be used (see -D/--print-default-template)


つまり --template が指定されたとき(一部推測込みになりますが)、順番は


  1. カレントディレクトリを見る


    • 指定されている文字通りのファイル名を探す

    • 拡張子が無い場合は、出力フォーマットに対応する拡張子を勝手に補う



  2. --data-dir で指定されるユーザデータディレクトリの中にある「templates」ディレクトリを探す

  3. デフォルトユーザデータディレクトリの中にある「templates」を探す

  4. Pandoc既定のデータディレクトリの中にある「templates」を探す

となるようです。


実験

筆者環境 (Windows 10 build 15063, pandoc 1.19.2.1) で実験してみました。


  • 特に意味はありませんが、Emacs org-mode (org)をフォーマットとして使います。

  • 注意:Chocolatey v0.10.5 で入れたのですが、Chocolatey上では「pandoc 1.19.2.1」というバージョンなのに、 pandoc -v では「pandoc 2.0」と表示されます。実際には前者の「pandoc 1.19.2.1」が入っていると思われます。


1. デフォルトユーザデータディレクトリにテンプレートを格納


  • Windowsなので %HOME%\AppData\Roaming\pandocdefault.org という名前でテンプレートファイルを作ります。

  • bodyの下に「AAA」と表示されるようにします。


default.org

$if(title)$                                             

#+TITLE: $title$

$endif$
$if(author)$
#+AUTHOR: $for(author)$$author$$sep$; $endfor$
$endif$
$if(date)$
#+DATE: $date$

$endif$
$for(header-includes)$
$header-includes$

$endfor$
$for(include-before)$
$include-before$

$endfor$
$body$
AAA
$for(include-after)$

$include-after$
$endfor$



2. 適当なディレクトリにテンプレートを作成

bodyの下に「BBB」と表示されるようにします。


tmp.org

$if(title)$

#+TITLE: $title$

$endif$
$if(author)$
#+AUTHOR: $for(author)$$author$$sep$; $endfor$
$endif$
$if(date)$
#+DATE: $date$

$endif$
$for(header-includes)$
$header-includes$

$endfor$
$for(include-before)$
$include-before$

$endfor$
$body$
BBB
$for(include-after)$

$include-after$
$endfor$



3. カレントディレクトリにtemplatesディレクトリを作成、その下にテンプレート作成


  • 2.のtmp.orgと同じディレクトリにtemplatesディレクトリを作成

  • その更に下にdefault.orgというテンプレートファイルを作成

  • bodyの下に「CCC」と表示されるようにします。


templates/default.org

$if(title)$

#+TITLE: $title$

$endif$
$if(author)$
#+AUTHOR: $for(author)$$author$$sep$; $endfor$
$endif$
$if(date)$
#+DATE: $date$

$endif$
$for(header-includes)$
$header-includes$

$endfor$
$for(include-before)$
$include-before$

$endfor$
$body$
CCC
$for(include-after)$

$include-after$
$endfor$


まとめると、こういう構造です。(実際に入れているファイルの一部は省略)

%HOME%/AppData/Roaming/pandoc/templates

└── default.org

カレントディレクトリ
├── templates
│   └── default.org
└── tmp.org


4. pandocコマンドを実行

$ echo '**hoge**' | pandoc -s -f markdown -t org  # (A)

*hoge*
AAA

$ echo '**hoge**' | pandoc -s -f markdown -t org --template tmp.org # (B)
*hoge*
BBB

$ echo '**hoge**' | pandoc -s -f markdown -t org --data-dir . # (C)
*hoge*
CCC

$ echo '**hoge**' | pandoc -s -f markdown -t org --data-dir . --template tmp.org # (D)
*hoge*
BBB

$ echo '**hoge**' | pandoc -s -f markdown -t org --template tmp.org --data-dir . # (E)
*hoge*
BBB


  • (A) デフォルトユーザデータディレクトリが指定された

  • (B) カレントディレクトリのテンプレート(tmp.org)が指定された

  • (C) カレントディレクトリの中のtemplatesディレクトリにあるdefault.orgが指定された

  • (D) カレントディレクトリのテンプレート(tmp.org)が指定された



    • --templateオプションが優先され、--data-dirオプションは無視された



  • (E) Dと同様(引数の順番を入れ替えたが、結果は同じだった)


5. 結論

先ほどのように

テンプレートを引数で指定 (--template) > ユーザデータディレクトリを引数で指定 (--data-dir) > デフォルトユーザデータディレクトリ

という優先順位を確認しました。(Pandoc既定のデータディレクトリについては実験を省略)

以上です。