【Rmarkdown】テーブル内の引用(@)がレンダリングされない問題の解決策 (Luaフィルタ)

Rmarkdown

QuartoやR Markdownを使用して論文やレポートを作成している際、kableExtra などのパッケージを用いてテーブルを作成することがあります。その際、セル内に引用(例:@citation)を含めても、それが正しくハイパーリンクや脚注としてレンダリングされず、単なる文字列として出力されてしまう問題に直面することがあります。

この問題は、Pandocの仕様、および使用しているパッケージがテーブルのセル内容を「Raw text(生のテキスト)」として出力してしまうことに起因しています。

問題の背景

通常、PandocはMarkdownとして文書を解析し、@citation を適切な引用要素に変換します。しかし、kableExtra などによって生成されたテーブルのコンテンツが、Pandocにおいて RawBlock や RawInline として渡されてしまうと、Pandocの標準的な引用処理(Citation Processor)をバイパスしてしまいます。

実際に、QuartoのGitHub DiscussionsやPositのフォーラムでも、このような「テーブル内での引用の扱い」に関する議論がいくつか見られます。

解決策:Luaフィルタ fix_kable_citation.lua

この問題を解決するために、Raw textの中に含まれる引用シンボル(@)を検出し、その部分だけを再度Markdownとして再解析してPandocの要素に変換するLuaフィルタを作成しました。

仕組み

このフィルタは以下のステップで動作します。

  1. ターゲット形式の判定: HTML (htmlhtml5 等) および LaTeX (latextex) の Raw要素を対象とします。
  2. 引用シンボルの検出: テキスト内に @ が含まれているか、あるいは [@citation] のようなブラケット形式が含まれているかをチェックします。
  3. 再解析@ を含む部分を抽出し、pandoc.read(text, "markdown") を用いて、その部分だけをMarkdownとして読み込みます。
  4. 要素の置換: 解析された引用要素を、元のRaw textの代わりに挿入します。

これにより、テーブル内であっても通常の本文と同様に、引用が正しくレンダリングされるようになります。

実装コード (fix_kable_citation.lua)

local function is_target_format(fmt)
  return fmt == "latex" or fmt == "tex"
      or fmt == "html" or fmt == "html4" or fmt == "html5"
end

local function split_citations(text, fmt)
  local inlines = pandoc.List({})
  local buf = {}
  local i = 1
  local n = #text

  local function flush_buf()
    if #buf > 0 then
      inlines:insert(pandoc.RawInline(fmt, table.concat(buf)))
      buf = {}
    end
  end

  while i <= n do
    local c = text:sub(i, i)
    
    -- [@citation] の処理
    if c == "[" then
      local bracket = text:match("^%b[]", i)
      if bracket and bracket:match("@[%w]") then
        local ok, parsed = pcall(pandoc.read, bracket, "markdown")
        if ok and #parsed.blocks > 0 then
          flush_buf()
          for _, blk in ipairs(parsed.blocks) do
            if blk.content then
              for _, il in ipairs(blk.content) do
                inlines:insert(il)
              end
            end
          end
          i = i + #bracket
          goto continue
        end
      end
    end

    -- @citation 形式の処理
    if c == "@" then
      local prev = text:sub(i - 1, i - 1)
      if i == 1 or not prev:match("[%w\\]") then
        local bare = text:match("^@[%w][%w%-_:.]*", i)
        if bare then
          local ok, parsed = pcall(pandoc.read, bare, "markdown")
          if ok and #parsed.blocks > 0 then
            flush_buf()
            for _, blk in ipairs(parsed.blocks) do
              if blk.content then
                for _, il in ipairs(blk.content) do
                  inlines:insert(il)
                end
              end
            end
            i = i + #bare
            goto continue
          end
        end
      end
    end

    table.insert(buf, c)
    i = i + 1
    ::continue::
  end
  flush_buf()
  return inlines
end

function RawBlock(el)
  if not is_target_format(el.format) then return nil end
  if not el.text:find("@") then return nil end
  local inlines = split_citations(el.text, el.format)
  return pandoc.Plain(inlines)
end

function RawInline(el)
  if not is_target_format(el.format) then return nil end
  if not el.text:find("@") then return nil end
  local inlines = split_citations(el.text, el.format)
  return inlines
end

使い方

作成した .lua ファイルを、プロジェクトのディレクトリに配置します。

その後、Quarto/R Markdown の YAML ヘッダーに以下のように追加します。

QuartoのYAMLヘッダー

---
title: "My Research Document"
format: html
filters:
  - fix_kable_citation.lua
---

RmarkdownのYAMLヘッダー

---
title: "My Research Document"
output:
  html_document:
    pandoc_args:
      - "--lua-filter=fix_kable_citation.lua"
---

これで、kableExtra 等から出力された Raw Text 内の引用が、適切にレンダリングされるようになります。

まとめ

テーブル内での引用問題は、出力形式が Raw Text になることで Pandoc の解析プロセスから外れてしまうことが原因でした。

このLuaフィルタを使用することで、複雑な設定変更をすることなく、既存のワークフローの中でスマートに問題を解決できます。

広告

Amazonアフィリエイトでブログ運営しています。
応援いただけると嬉しいです。


ネスカフェ 香味焙煎 ひとときの贅沢 スティック ブラック 20P,箱,レギュラー ソリュブル コーヒー,個包装


AHMAD TEA(アーマッドティー) クラシックセレクション ティーバッグ 20袋

タイトルとURLをコピーしました