「Pandoc Advent Calendar 2019」を埋めてみた。
アドベントカレンダー1日目の記事では、Pandocにおける「テンプレート変数」と「メタデータ」について詳しく解説されている。
- Pandocのテンプレート機能でYAMLから本の奥付を自動生成する(Qiita:@sky_y)
そこには以下のような記述がある。
3. Pandocフィルタはメタデータにアクセスできるが、テンプレートの変数にはアクセスできない
しかし、実際に(LaTeX出力用の)フィルタを実装していると、「フィルタからテンプレート変数を設定したい」場合が時々ある。
LaTeXに変換する場合に、フィルタにおいて
— 某ZR(ざんねん🙃) (@zr_tex8r) 2019年11月30日
\usepackage{scsnowman}
のように特定のパッケージを読み込ませたいことがあるのだけど、それを確実に(つまりユーザが -H オプションや header-includes 変数を使ったか否かに依らずに)行う方法はない、ということか。#ぱんどっく
もちろん、当該の記事に
「テンプレート変数を与えるために、YAMLメタデータブロックで変数(=メタデータ)を定義する」というテクニックが便利です。
と書かれているように、「テンプレート変数の代わりにメタデータを使う」というテクニックは存在する。でも果たしてそれはいつでも通用するのだろうか。
Pandocのマニュアルには次のようにある。(下線は筆者による)
emplates contain variables, which allow for the inclusion of arbitrary information at any point in the file. They may be set at the command line using the-V/--variable
option. If a variable is not set, pandoc will look for the key in the document’s metadata, which can be set using either YAML metadata blocks or with the-M/--metadata
option.
これを読む限りは「テンプレート変数の代わりにメタデータを使う」というテクニックは「ユーザが当該のテンプレート変数を(-V
オプションなどで)設定していない」ことが前提になりそうである。
調べてみた
※最新版(2.8.0.1版)のPandocを用いた。
[2019/12/14追記] 2.8.0.1版の挙動の一部は過去版の挙動と食い違っていて、どうやらこれは“不具合”だったようです。2019/12/05リリースの2.8.1版で“修正”されたため、この版の動作に基づいて記述を修正しました。
次のテンプレートを用意する。
$for(foo)$$foo$$endfor$
※複数の値が指定されることを見越してループにしておく。
このテンプレートを用いて、空のMarkdownファイル“empty.md”をHTMLに変換することを考える。
pandoc empty.md --template=foo.html -o out1.html
その際に、テンプレート変数・メタデータの設定のオプションの組合せとして、次の4つを試してみる。
- ①追加オプションなし。
- ②
-V foo=V1
: テンプレート変数のfoo
を設定する。 - ③
-M foo=M1
: メタデータのfoo
を設定する。 - ④
-V foo=V1 -M foo=M1
: テンプレート変数とメタデータの両方を設定する。その結果
変数 | メタデータ | 出力 | |
---|---|---|---|
① | なし | なし | (空) |
② | V1 |
なし | V1 |
③ | なし | M1 |
M1 |
④ | V1 |
M1 |
V1 |
マニュアルの記述の通りで、テンプレート変数が既に設定されている場合は、「メタデータを代わりに設定する」という技法は使えない、という結果になった。
もう少し調べてみる
結論は出たが、事のついでとして、もう少し複雑な場合を調べてみることにする。
複数の値を指定してみた
先の実験と同じ設定で、-V
・-M
オプションを反復することで複数の値を指定してみることにする。
- ①追加オプションなし。
- ②
-V foo=V1 -V foo=V2
- ③
-M foo=M1 -M foo=M2
- ④
-V foo=V1 -M foo=M1 -V foo=V2 -M foo=M2
結果は以下の通り。
変数 | メタデータ | 出力 | |
---|---|---|---|
① | なし | なし | (空) |
② | V1 とV2 |
なし | V1V2 |
③ | なし | M1 とM2 |
M1M2 |
④ | V1 とV2 |
M1 とM2 |
V1V2 |
- やはりテンプレート変数が設定済の場合はメタデータの値は使われない。
テンプレート変数は1つの値しか保持できない。テンプレート変数とメタデータのいずれもオプション(-V
や-M
)を繰り返すことで複数の値を保持できる。
※2.8版~2.8.0.1版ではテンプレート変数が1つの値しか持てないという“不具合”があった。
header-includesも試してみた
header-includes
変数については、「テンプレート変数を指定する」「メタデータを指定する」の他に「ヘッダファイルを指定する(-H
オプション)」という方法も存在する。
- 先ほどのテンプレートで
foo
をheader-includes
に置き換えた新しいテンプレート“header.html”を用意する。 H1
だけ書かれたファイル“h1”とH2
だけ書かれたファイル“h2”を用意する。-V
・-M
・-H
の各々について「2つ指定する」「指定しない」としたものの全ての組合せ(8通り)を試してみる。
例えば、-V
・-M
・-H
の全てを2つ指定する場合のコマンド行は以下のようになる。
pandoc empty.md --template=header.html -o out2.html \ -V header-includes=V1 -M header-includes=M1 -H h1 \ -V header-includes=V2 -M header-includes=M2 -H h2
結果は以下の通り。
変数 | メタデータ | ヘッダファイル | 出力 |
---|---|---|---|
なし | なし | なし | (空) |
V1 とV2 |
なし | なし | V1V2 |
なし | M1 とM2 |
なし | M1M2 |
V1 とV2 |
M1 とM2 |
なし | V1V2 |
なし | なし | h1 とh2 |
H1H2 |
V1 とV2 |
なし | h1 とh2 |
V1V2H1H2 |
なし | M1 とM2 |
h1 とh2 |
H1H2 |
V1 とV2 |
M1 とM2 |
h1 とh2 |
V1V2H1H2 |
- テンプレート変数での指定とヘッダファイルでの指定は共存する。それらの少なくとも一方が指定済の場合はメタデータの値は使われない。
- ヘッダファイルは複数指定できる。
- テンプレート変数の値(
高々1つ指定順で複数)の後にヘッダファイルの値(指定順で複数)が並ぶ。
まとめ
やっぱり、「フィルタの実装の内部でheader-includes
を設定する」ための確実な方法は存在しない?
(画像は本文と関係ありません)