user icon

md2reviewでMarkdownからRe:VIEWに変換すると箇条書きの入れ子は無視される

※2014年7月頭の状況です(解決済です次の記事をご参照ください)。

MarkdownとRe:VIEWを知っている方しか見ない記事だと思うので細かい説明は抜きでいきます。

ソフトウェア技術者だと最近はMarkdownで資料を書くことが多いのではないかと思います。
私もそうなのですが、Markdownで書いたお資料を印刷する際に困っていました(客様に渡す報告書等)。

いろいろ試した結果Mou(Mac用のMarkdownエディター)で印刷していたのですが、フォントが中国系になるという不満点がありました。

そこでmd2reviewでMarkdownからRe:VIEWに変換してPDF化するというフローを試したところかなりいい感じに出力できたのですが、私にとっての大問題が判明しました。

そう、 「md2reviewでMarkdownからRe:VIEWに変換すると箇条書きの入れ子は無視される」 のです。

番号付き箇条書き(Decimal型)についてはRe:VIEW側が対応していないのでしょうがないのですが(個人的にあまり使ってないですし)、普通の箇条書き(Disc型)の入れ子は多用しているので大変厳しい。

そこで、改造して Pull Request 出したろう!と着手したのですが、

  • md2review調査
    • かなりの部分redcarpet依存であると判明。
  • redcarpet調査
    • 「メイン処理はCで書かれてるのかよ〜」と思いつつ読んだ結果、簡単には対応できそうもないと判断。なぜmd2reviewが箇条書きの入れ子に未対応なのか、納得する。
  • 残念対応
    • md2reviewで変換したRe:VIEWの箇条書き部分に入れ子の状態から挿入される空行に規則性があるようだったので、これを解析したらどうにかならないか?と思う。
    • redcarpetにpostprocessというレンダリングの後処理を行うための仕組みがあったのでそこで処理してみた。
    • 作ってみた結果、以下の問題がある残念なものが出来上がった。
      • 入れ子2段までしか判断できないため3段以上は正常に動作しない。
      • 箇条書きの最後のアイテムについては判断できないためとりあえずワーニングコメント#@warn(CONFIRM NEST!)を出力する(確認しやすくはなった)。

という残念な結果に終わりました。

残念対応ですが無いよりはマシですので公開しておきます。

md2reviewのreview.rbに以下のpostprocessを追加します。

def postprocess(full_document)
  # レンダリング結果を解析して順番なし箇条書きのインデントを反映する。
  # ただし2段までしか判断できないため3段以上は正常に動作しない。
  # また、箇条書きの最後のアイテムについては判断できないため
  # ワーニングコメント#@warn(CONFIRM NEST!)を出力する。

  # 無効化するときは次の行のreturnを有効化する。
  #return full_document
  require 'set'

  lf_del_set = Set.new
  lf_ins_set = Set.new

  pre_ul_flg = false
  pre_emp_flg = false
  ul_level = 1
  
  lines = []
  full_document.split(/\n/).each_with_index do |line, i|
    if line == "" || line[0, 3] == " * " 
      if line == ""
        if pre_emp_flg && pre_ul_flg
            line = "\#@warn(CONFIRM NEST!)\n"
        elsif pre_ul_flg
          lf_del_set.add(i)
          if 1 < ul_level
            if lines[i - 1][0, 2] == " *" && lines[i - 2][0, 2] == " *"
              lines[i - 1] = " #{'*' * (ul_level - 1)} " +
                lines[i - 1][(ul_level + 2), lines[i - 1].length]
              ul_level -= 1
              lf_ins_set.add(i - 1)
            end
          end
        end
        
        pre_emp_flg = true
      else
        if pre_ul_flg && pre_emp_flg
          ul_level += 1
        end

        line = " #{'*' * ul_level} " + line[3, line.length]

        pre_ul_flg = true
        pre_emp_flg = false
      end
    else
      pre_ul_flg = false
      ul_level = 1
    end
    
    lines.push(line)
  end
  if pre_ul_flg
    lines.push("\#@warn(CONFIRM NEST!)\n")
  end
  
  ret_document = ""
  lines.each_with_index do |line, i|
    if lf_ins_set.include?(i)
      ret_document += "\n"
    end
    if !lf_del_set.include?(i)
      ret_document += line + "\n"
    end
  end
  ret_document
end

以上まではほぼMarkdownで記述し改造版md2reviewで変換した後、箇条書き部分の入れ子が3段の箇所と箇条書きの最後をて修正したRe:VIEWファイルから出力したHTMLです(ソースコード掲載の箇所はハイライト表示の関係で書きなおしました)。

同じファイルから作ったPDFが以下のファイルです(単純なレポート用に調整した設定を使用しています)。
report.pdf

EPUBも作ったんですが、セキュリティ制約でアップロードを弾かれちゃいました。残念。

このように様々な出力が可能という利点はあります。

余談ですがEPUB出力するのはとても簡単です。PDF出力はLaTeX環境をつくらなければならないのでやや面倒でした。

Facebooktwitterlinkedintumblrmail

Trackback

Comment

> いろいろ試した結果Mou(Mac用のMarkdownエディター)で印刷していたのですが、フォントが中国系になるという不満点がありました。

私はMouのCSSを調整して日本語フォントを使うようにしましたー。

本当だ。MouのCSSでのフォント設定、検索すると出てきますね。

Mou後継を目指しているらしいMacDownでも綺麗に印刷が出たので印刷に関してはそれでもいいかな、と思ったりもしします。

Re:VIEW化は別の利点もあると思いますが、活かせるのは電子書籍を書く場合くらいですかね。

名前
E-mail
URL
コメント

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)