マークダウンから爆速で EPUB をつくる

日ごろからマークダウン形式で何かを書かれているエンジニアの方は、多いかと思います。
かくいう私もその内の一人なのですが、ときおり外出先などで読みたくなったります。
その際、そのままマークダウン形式で読んでも良いのですが、電子書籍にするとより見やすくすることができます。

そこで今回は、Pandoc を使用してマークダウン形式のファイルから EPUB ファイルを爆速でつくる手順について紹介しようと思います。

## 1. Pandoc のインストール

Pandoc がまだインストールされていない場合は、[Pandoc の公式サイト](https://pandoc.org/installing.html) からインストールします。

## 2. マークダウンファイルの準備

マークダウンファイルを用意します。
今回は、過去の Qiita 記事から作成した以下のような内容のマークダウンファイル（`python-blockchain.epub`）を使用します。

````markdown
# 1. はじめに
本記事では Python を使用してブロックチェーンを学ぶ方法について解説していきます。ブロックチェーンは仮想通貨の分野でよく知られていますが、その技術は多岐にわたる分野で活用されています。今回は、Python の基礎知識がある方を対象に、ブロックチェーンの仕組みと Python での実装方法について詳しく解説していきます。
...
## 2.1. ブロックの概要
ブロックは、ブロックチェーンの基本的なデータ構造であり、用途によって要素が変動することが想定されますが、本記事では、以下の要素で説明していこうと思います。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2659159/4979353d-929c-36d2-ff59-e27c7bb4bbcf.png" width="230px"/>

| 要素 | 説明 |
|:--|:--|
| ヘッダー | ブロックの情報を識別するためのデータ。 |
| トランザクション | トランザクションの詳細が含まれている。 |
| ブロックハッシュ値 | ブロックの一意の識別子であり、ブロックのデータが改ざんされていないことを確認するために使用される。 |
...
```python
class Block:
    def __init__(self, index, timestamp, nonce, previous_block_hash, merkle_root, transactions):
        self.index = index  # インデックス
        self.timestamp = timestamp  # タイムスタンプ
        self.nonce = nonce  # ナンス
        self.previous_block_hash = previous_block_hash  # 前のブロックハッシュ値
        self.merkle_root = merkle_root  # マークルルート```
```
...

````

## 3. EPUB への変換

以下のコマンドを使用して、マークダウンファイルを EPUB に変換します。
この際、マークダウン内の画像 URL から画像を自動的にダウンロードし、EPUB ファイル内から参照できるようにします。

:::note warn
**留意点:**
ここでは、見やすくするため、ハット (`^`) を入れて複数行に分けて書いています。
:::

```
pandoc python-blockchain.md -o python-blockchain.epub ^
--split-level=1 --extract-media=./media ^
--metadata title="Python で学ぶブロックチェーン"
```

- **`python-blockchain.md`**: 変換するマークダウンファイルの名前
- **`python-blockchain.epub`**: 出力される EPUB ファイルの名前
- **`--epub-chapter-level=1`**: H1 見出しを章のタイトルとして使用する
- **`--extract-media=./media`**: マークダウンファイル内の画像 URL から画像をダウンロードし、EPUB ファイル内で使用するために現在のディレクトリに抽出する
- **`--metadata title="Python で学ぶブロックチェーン"`**: 文書のタイトルを指定する

このコマンドを実行することで、マークダウンファイルが EPUB 形式に変換され、指定した画像が EPUB ファイル内で使用されるようになります。

:::note warn
**留意点:**
- マークダウンファイル内の画像 URL はインターネット上に存在し、アクセス可能である必要があります。
- Pandoc のバージョンによってはオプションが異なる場合がありますので、詳細は [Pandoc のマニュアル](https://pandoc.org/MANUAL.html)を参照してください。
:::

## 4. 作成した EPUB ファイルの確認
EPUB リーダーを使用して、作成した EPUB リーダーの表示を確認します。

<ins>文書のタイトル</ins>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2659159/519af81b-96ef-4a65-ff12-880f8df4ca1c.png" width="400px" />

<ins>見出し、画像</ins>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2659159/d2773ed4-9b34-f13b-ff68-0c3ef92c54ba.png" />

<ins>表、コードブロック</ins>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2659159/6f0f9b3b-bacd-133a-2870-c480a72a6c69.png" width="400px" />

## 5. CSS スタイルの適用
必要に応じて、CSS ファイルで表示スタイルを変更することもできます。

今回は、以下のような CSS ファイル (`style.css`) を作成しました。
```
/* タイトル */
title {
    font-size: 4em;
    font-weight: bold;
}

/* H1見出し */
h1 {
    font-size: 1.5em;
    font-weight: bold;
}

/* 本文 */
body {
    font-size: 1em;
}

/* コードブロック */
pre, code {
    background-color: #f5f5f5;
}
code {
    font-family: Menlo, Consolas, 'DejaVu Sans Mono', monospace;
    font-size: 0.9em;
}
```

EPUB を作成する際は、`--css FILE` オプションを付与して実行します。
```
pandoc python-blockchain.md -o python-blockchain.epub ^
--split-level=1 --extract-media=./media ^
--metadata title="Python で学ぶブロックチェーン" ^
--css style.css
```

:::note info
**Note:**
EPUB リーダーによっては、想定通り表示されなかったりするようなのでご注意ください。
:::

### 余談

私の場合、Windows の EPUB リーダーでは、うまくスタイル表示がされませんでした。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2659159/7552ea5d-eb8a-c86b-4f98-03ea7b8d9682.png" width="450px" />

一方、スマホではコードブロックのスタイル適用はできているように見えました。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2659159/5e5236c6-9b16-d564-40de-c61d57120745.jpeg" width="400px" />

EPUB ファイルの拡張子を `.zip` に変更して中身を確認すると、スタイルの適用自体は正しく行われているようです。うーん、不思議…

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2659159/4eb05f44-4845-ed98-46f1-adaa8847373b.png" width="550px" />