GravのプラグインImage CaptionsでBootstrapのfigureクラスをあてる方法を紹介します。ImageCaptionsというプラグインはGravでは紫のチェックマークがついています。一応Gravの公式という意味でついているのですが、このプラグインの実装方法は少々疑問でした。これについてはページの最後に記載しています。

ImageCaptionsで出来ること

この説明はpluginのREADMEままです。Gravのマークダウンから次のように画像を埋め込みます。

![My Image Alt Text](myimage.jpg?classes=caption "My Image Caption")

ImageCaptionsプラグインがオフの場合は次のようにレンダリングされます。

<img src="/yourpage/mymage.jpg" alt="My Image Alt Text" title="My Image Caption" class="caption" />

ImageCaptionsプラグインをオンにするとこれが次のようになります。

<figure class="image-caption">
    <img src="/yourpage/mymage.jpg" alt="My Image Alt Text" title="My Image Caption" class="caption" />
    <figcaption>My Image Caption</figcaption>
</figure>

あとはクラス名等適当に指定してスタイルあててこっちでよろしくやってくださいよ、というプラグインです。

Bootstrapをあてる場合

enabled: true
built_in_css: false
scope: img.figure-img
entire_page: false
figure_class: figure
figcaption_class: figure-caption

figure_classfigcaption_classの設定はページごとにもfrontmatterで変更できます。build_in_cssはこれからbootstrapのスタイルをあてるのでオフにします。.figureをつかうときには必ず.figure-imgを指定するのでscopeはこのように設定しました。

![Image of forest](forest.jpg?classes=figure-img,img-fluid&width=50% "This is a beautiful forest")

レンダリングされた見た目はこのようになります。


Image of forest
Figure: This is a beautiful forest

このHTMLは次です(多少違いますが)。

<figure class="figure figure-img">
  <img width="50%" title="This is a beautiful forest" alt="Image of forest" class="img-fluid figure-img" src="forest.jpg">
  <figcaption class="figure-caption">This is a beautiful forest</figcaption>
</figure>

どうやらfigure-imgが意図していないfigureタグに追加されてしまうようです。これはよくわかりません。READMEとは違う動作なのでバグでしょうね。

画像ごとにマークダウンからclasseseは指定できるのでborderなどbootstrapで使えるクラスをうまいこと指定できます。figure-captionのクラスについてはfrontmatterからページごとに1パターンしか指定できないのでページごとに統一されます。

ImageCaptionsの実装方法の問題点

何をするプラグインなのかはわかりました。ところが実装を見てみたところ一見して効率は良くなさそうです。専門家ではないので違っていても許してね。簡単に説明すると吐き出された生HTMLをパースして一度DOMツリーを構成、その中からscopeオプションで指定されたタグ、クラス、idにより特定の要素をセレクトし、figureタグを挿入してDOMツリー再構成、そののちにHTMLに戻しています。DOMツリーは外部ライブラリを使っています。

ちなみにページ全体のHTMLをパースするかページのマークダウン単位でHTMLをパースするかをentire_pageオプションで指定できます。テンプレート内(例えばbase.html.twig)にimg.captionが無いことがわかっていればオンにする意味は無いし、あったところでテンプレートに直接<figure>を書いておけばいいのでこのオプションは全くもって不要ではないでしょうか。

HTMLをパースするなんてブラウザで日常的にやっているそんなに重い処理ではないといわれればそれまでなんですけどこれは気になります。なぜかというと、このやり方であると、例えばこれと同じようなプラグインがあったとすると、そのプラグインごとのonPageContentProcessedでほとんど同じDOMツリーを別々に構築することになってしまいます。この場合明らかに一度DOMツリーを構成してから全てのプラグインがやりたい処理を施し、再びHTMLに戻したほうが高効率です。scopeの要素を取ってくるんだからしっかりパースした方がregexしたりするより効率いい、という可能性とは関係なく。

コンテンツのimgタグを検索してそこをfigureで囲んで適切なアトリビュートを設定する、という機能はonMarkdownInitializedにてParseDownのブロックを上書きする方法の方が効率がよさそうに思います。が、onMarkdownInitializedで、新しいブロックやインラインを追加しているpluginはよくあるのですが上書きしている例を探しているのですがまだ見つけられていません。ということはそういったやり方が現状できないということなのでしょうか。出来てしかるべきだとは思うんですけどね。キーとなるのはelementToHtmlメソッドでしょうか。これについて要調査。

あと1点言いたいことはデフォルトでDOMツリーの構成まで処理が走ってしまうことですね。どういうことかというと

image-captions:
    enabled: false

でページのfrontmatterからプラグインの処理は中断させられるのですが、デフォルトではページごとの設定はオンであるのでImage Caption要素が無いページでは毎回このオプションをfrontmatterに書かなければなりません。オフのページの方が多いサイトではこれは問題です。プラグインそのものをオフにしてしまうと

image-captions:
    enabled: true

をしても機能は働きません。従ってfrontmatterの宣言が無い場合のデフォルトをプラグイン側のオプションとして設けなければこの問題は解決できません。ひとつ言えるのは、どのページでもキャプション画像無いならプラグインをオフにすべきということでしょうね。

英語で書いてない時点でアレなんですけど、これを見た誰か、修正してくれないかな~。以上。