Markdown から EPUB を生成する方法の備忘録です。
Markdown から EPUB に変換するなら、md2review + Re:VIEW という強力な組み合わせがあります。
- Re:VIEW - Digital Publishing System for Books and eBooks
- takahashim/md2review: a converter from Markdown into Re:VIEW, using redcarpet
…が、今回は gepub という gem を使って生成してみました。 ここにあげたコードは gist.github.com に全体をアップしています。
gem を用意する
今回利用する gem、ライブラリです。
- redcarpet
- Markdown から HTML への変換に利用します。
- nokogiri
- HTML から XHTML への変換と要素の編集に利用します。
- securerandom
- EPUB に設定する identifier の値を生成するために利用します。
- gepub
- EPUB の生成に利用します。
require 'redcarpet' require 'nokogiri' require 'securerandom' require 'gepub'
Markdown を分割する
今回はレベル 1 の見出しを各章の始まりとして扱うようにしました。 そのためまず一つの Markdown を章単位に分割していきます。 なおこのコードでは、レベル 1 の見出しがないケースや、レベル 1 の見出しと見出しの間に文章が含まれないようなケースは考慮してません。
Chapter = Struct.new("Chapter", :title, :body) def split_md_into_chapters(source) result = [] title = nil begin body, next_title, rest = source.partition(%r{^# .*\n}) result << Chapter.new(title, "# #{title}\n#{body}") unless body.empty? title, source = next_title[2..-2], rest end until source.empty? result end
各章を XHTML に変換する
redcarpet を利用して Markdown から HTML に変換し、nokogiri を利用して HTML から XHTML に変換しています。 redcarpet にも XHTML 形式で出力する機能がありますが、EPUB で利用するには厳密に XML の仕様にそっている必要があるようです。 また EPUB のビューアによっては title 要素が含まれていないと正しく表示できないようです。ここではレベル 1 の見出しの内容をタイトルとして設定しています。
def convert_to_xhtml(chapter) markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML.new, autolink: true, tables: true, fenced_code_blocks: true, prettify: true) html = Nokogiri::HTML.parse( markdown.render(chapter.body)) html.title = chapter.title html.to_xhtml end
XHTML をまとめて EPUB で出力する
gepub を利用して EPUB を生成します。 ここで identifier の設定は必須になっていますが空文字列でも生成できました。ただしビューアによっては identifier が空文字列になっていると正しく表示できないようです。
画像ファイルを含めたい場合は book.ordered
ブロックの外で book.add_item
を利用して追加します。このとき指定するパスはテキスト内から参照しているパスと合わせる必要があります。
また各ファイルを格納するパスは book.add_item("#{index}.xhtml")
のように直下に配置してしまうと、ビューアによってはうまく参照できないようで、コードのようにサブディレクトリに配置するのがよさそうです。
book.generate_epub
で EPUB をファイルに出力しています。
book.generate_epub_stream
を利用するとファイルでなくストリームとして結果を取得できます。このとき結果として返るストリームはカーソルが終端に移動したままになっているようなので、seek(0)
で先頭に移動してから読み出す必要があります。
def generate_epub_from_markdown(source) book = GEPUB::Book.new {|book| book.identifier = "md2epub-#{SecureRandom.uuid}" book.title = 'md2epub - Markdown から EPUB を作る' book.ordered do split_md_into_chapters(source).each_with_index do |chapter, index| xhtml = convert_to_xhtml(chapter) book.add_item("text/#{index}.xhtml") .add_content(StringIO.new(xhtml)) .toc_text(chapter.title) end end } book.generate_epub('md2epub.epub') end
実行
generate_epub_from_markdown(<<~MARKDWON)
# Markdown を分割する
レベル 1 の見出しを一つの章として扱うように分割します。
# 各章を XHTML に変換する
gem [redcarpet](https://github.com/vmg/redcarpet) と [nokogiri](https://www.nokogiri.org) を利用して markdown から XHTML に変換します。
```ruby
markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML.new, autolink: true, tables: true)
html = Nokogiri::HTML.parse(markdown.render(source))
html.title = title
xhtml = html.to_xhtml
```
# XHTML をまとめて EPUB で出力する
gem [gepub](https://github.com/skoji/gepub) を利用して XHTML から EPUB に変換します。
MARKDWON
生成された EPUB ファイルを Mac 標準の Books で開くとこのように表示されます。
いつか読むはずっと読まない:Wunderkammer
もう展示は終わってしまいましたが、とても興味深い内容でした。
- 作者: 早良朋
- 出版社/メーカー: 小学館
- 発売日: 2017/07/12
- メディア: コミック
- この商品を含むブログ (14件) を見る
- 作者: 早良朋
- 出版社/メーカー: 小学館
- 発売日: 2018/03/12
- メディア: コミック
- この商品を含むブログ (7件) を見る
- 作者: 早良朋
- 出版社/メーカー: 小学館
- 発売日: 2018/12/12
- メディア: コミック
- この商品を含むブログ (1件) を見る