PandocでHTML出力時に見出し番号を第1章とか第1.1節とかしたい

カテゴリ: pandoc

Pandocアドベントカレンダー12月23日の記事です。

1 はじめに

PandocでたとえばmarkdownをHTMLに出力する時、--number-sectionsオプションを与えると、見出しに番号が付きます。

雰囲気的には

# 見出し1

## 見出し1-1

1 見出し1

1.1 見出し1-1

になる感じ。

ただし、「第1章」とか「第1.1節」みたいなことはできません。

どうする?

選択肢としては大きく3つあるでしょう。自身が使える言語とかに合わせて選択してください。本記事ではJavaScriptを採用してます。

方法 Luaフィルタ CSS JavaScript
目次対応
出力形式 任意 HTML HTML
番号操作 ×
--number-offset ×

2 実装

2.1 Luaフィルタ

もっとも簡単な方法は、ZRさんのLuaフィルタを使うことでしょう。

https://gist.github.com/zr-tex8r/16967b9f3ed414bb8ceeca2962a7a9c2

wget --quiet https://gist.githubusercontent.com/zr-tex8r/16967b9f3ed414bb8ceeca2962a7a9c2/raw/56398449fa2f554de3937e7c7727d02385eda2bb/numbering.lua
echo -e "# foo\n\n## bar" | pandoc -t html --number-sections --lua-filter numbering.lua
## <h1 data-number="第1章" id="foo"><span class="header-section-number">第1章</span> foo</h1>
## <h2 data-number="第1節" id="bar"><span class="header-section-number">第1節</span> bar</h2>

Luaを使うメリット・デメリットは以下の通り。

  • メリット
    • 目次にも章・節の番号が反映される
    • 改造すれば--number-sectionsオプションに非対応なフォーマットもサポート可能
    • 改造すれば節番号が1.1の時1になってしまうのを防げる
  • デメリット
    • --number-offsetオプションで章番号をずらすことができなくなる

2.2 CSS

以下のCSSを<style>要素内に書くか、外部ファイルに記述して--cssオプションで取り込む。これも簡単。

:not(.unnumbered) h1:not(.unnumbered) .header-section-number:before,
:not(.unnumbered) h2:not(.unnumbered) .header-section-number:before {
  content: '第'
}
:not(.unnumbered) h1:not(.unnumbered) .header-section-number:after {
  content: '章'
}
:not(.unnumbered) h2:not(.unnumbered) .header-section-number:after {
  content: '節'
}

:not(.unnumbered)が2回登場するのは--section-divs対策です。

  • メリット
    • 目次にも章・節の番号が反映できる(が大変)
    • --number-offsetオプションに対応
  • デメリット
    • 節番号を1.1から1に変換できない
    • HTMLにしか使えない

2.3 JavaScript

JavaScriptを使うとCSSではできない節番号の操作も可能になります。あと、CSSにおける:before:afterの使い分けも不要になります。たぶんパフォーマンスはCSSに軍配が上がるでしょうが、無視できるほどのものでしょう。

document.addEventListener('DOMContentLoaded', function() {
  const h1 = document.querySelectorAll(
    ':not(.unnumbered) h1:not(.unnumbered) .header-section-number,' +
    '#TOC>ul>li>a>.toc-section-number');
  
  h1.forEach(function(x) {
      x.textContent = '第' + x.textContent + '章';
  });

  const h2 = document.querySelectorAll(
    ':not(.unnumbered) h2:not(.unnumbered) .header-section-number,' +
    '#TOC>ul>li>ul>li>a>.toc-section-number');

  h2.forEach(function(x) {
      x.textContent = '第' + x.textContent + '節';
  });
});
  • メリット
    • 目次にも章・節の番号が反映できる
    • --number-offsetオプションに対応
    • 節番号を1.1から1に変換できる
  • デメリット
    • HTMLにしか使えない

3 どれがいいのか

見出し番号の装飾方法を3通り紹介しました。基本はフォーマットに依存せず使えるLuaフィルタが便利だと思います。 --number-offsetオプションも使いたい場合で出力がHTMLなら、CSSやJavaScriptを検討しても良いでしょう。あるいは、--number-offsetを使うのをやめて、メタデータ経由で章番号をずらすよう、Luaフィルタに伝えるという手もありますね。

Enjoy!!