マクロツイーター

はてダから移行した記事の表示が崩れてますが、そのうちに直せればいいのに(えっ)

Pandocでメタデータはテンプレート変数になるかならないか

「Pandoc Advent Calendar 2019」を埋めてみた。

adventar.org

アドベントカレンダー1日目の記事では、Pandocにおける「テンプレート変数」と「メタデータ」について詳しく解説されている。

そこには以下のような記述がある。

3. Pandocフィルタはメタデータにアクセスできるが、テンプレートの変数にはアクセスできない

しかし、実際に(LaTeX出力用の)フィルタを実装していると、「フィルタからテンプレート変数を設定したい」場合が時々ある。

もちろん、当該の記事に

「テンプレート変数を与えるために、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版で“修正”されたため、この版の動作に基づいて記述を修正しました。

次のテンプレートを用意する。

[foo.html]
$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

結果は以下の通り。

変数 メタデータ 出力
なし なし (空)
V1V2 なし V1V2
なし M1M2 M1M2
V1V2 M1M2 V1V2
  • やはりテンプレート変数が設定済の場合はメタデータの値は使われない。
  • テンプレート変数は1つの値しか保持できない。テンプレート変数とメタデータのいずれもオプション(-V-M)を繰り返すことで複数の値を保持できる。
    ※2.8版~2.8.0.1版ではテンプレート変数が1つの値しか持てないという“不具合”があった。

header-includesも試してみた

header-includes変数については、「テンプレート変数を指定する」「メタデータを指定する」の他に「ヘッダファイルを指定する(-Hオプション)」という方法も存在する。

  • 先ほどのテンプレートでfooheader-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

結果は以下の通り。

変数 メタデータ ヘッダファイル 出力
なし なし なし (空)
V1V2 なし なし V1V2
なし M1M2 なし M1M2
V1V2 M1M2 なし V1V2
なし なし h1h2 H1H2
V1V2 なし h1h2 V1V2H1H2
なし M1M2 h1h2 H1H2
V1V2 M1M2 h1h2 V1V2H1H2
  • テンプレート変数での指定とヘッダファイルでの指定は共存する。それらの少なくとも一方が指定済の場合はメタデータの値は使われない。
  • ヘッダファイルは複数指定できる。
  • テンプレート変数の値(高々1つ指定順で複数)の後にヘッダファイルの値(指定順で複数)が並ぶ。

まとめ

やっぱり、「フィルタの実装の内部でheader-includesを設定する」ための確実な方法は存在しない?

f:id:zrbabbler:20191214213336p:plain:w300
(画像は本文と関係ありません)