Web小説を縦書きにしてPaperwhiteで読みたい！

この記事は[TSG Advent Calendar 2024](https://adventar.org/calendars/10134)の8日目の記事です。

ちょくちょくスマホでWeb小説を読むことがあるのですが、せっかく画面の大きいPaperwhiteを持っているのでこちらに移してみたところ、縦書きにした方がより読みやすいのではないかと思いました。
Web小説のサイトにも縦書きに変更する機能がついていることがあるのですが、変換がバグっていることがあり、またPDF化されているため細かい調整を自分で施すことができなかったため、Paperwhiteにインポートするところまでを自前で行う方法を調べてみました。またその過程で縦書き化の際にはまったポイントを紹介します。

# Paperwhiteにインポートするまでの流れ
PDFやEPUBなど既に完成している電子書籍の場合は、[Send to Kindle](https://www.amazon.co.jp/sendtokindle)を利用するとインターネット経由でPaperwhiteに転送することができます。また、パソコンに接続することで、エクスプローラーを通してPDFなどのファイルを直接転送することも可能です。ケーブルをつなぐなどの手間を除けば、すぐに反映される直接転送の方が便利なのですが、一般に流通している電子書籍フォーマットであるEPUBには対応していません（Send to Kindleなら可能です）。手動で電子書籍を作成する場合もこのEPUBの方が扱いやすいのでどうにかできないかと調べたところ、[Kindle Previewer](https://kdp.amazon.co.jp/ja_JP/help/topic/G202131170)というAmazon提供のソフトウェアを見つけました。これを利用することで、EPUBを読み込み表示のプレビューができるうえ、Paperwhiteが読み込めるMOBI形式に変換することができます。これを使って以下のようにWeb小説をPaperwhiteにインポートすることにします。

1. PythonスクリプトでHTML形式の小説をEPUB形式に変換する
2. Kindle PreviewerでMOBI形式に変換する
3. Paperwhiteと接続しMOBIファイルを転送する

以降の章ではステップ1を詳細に見ていきます。

# EPUB形式への変換
EPUBは国際的に使われている電子書籍フォーマットの一つで、XMLベースで記述されたファイルをzipで固めたものです。内部のフォーマットはこちらの記事が詳しいです。

https://qiita.com/ryota_haga/items/d9b2efb45337544bb9ff

Pythonでは[EbookLib](https://github.com/aerkalov/ebooklib)というライブラリがあり、これを操作してEPUBファイルを生成できます。

```sample.py
from ebooklib import epub
from pathlib import Path

book = epub.EpubBook()
book.set_identifier("bookid")
book.set_title("書籍タイトル")
book.set_language("ja")
book.add_author("著者", role="aut", uid="creator01")

# 表紙画像の登録
book.set_cover("cover.jpg", Path("cover.jpg").read_bytes())

# 各ページに追加することでこのcssを適用できる
css = epub.EpubItem(
    file_name="bookstyle.css",  # EPUB内の別のxmlから参照するときはここで指定されたファイル名を使う
    media_type="text/css",
    content=Path("bookstyle.css").read_text(),
)

# 目次ページ。book.tocに登録されたページのタイトルに従って生成される。
nav = epub.EpubNav()
nav.add_item(css)
spine = [nav]

page = epub.EpubHtml(
    title="第一話",
    file_name="1.xhtml",
    lang="ja",
    content=Path("1.xhtml").read_text(encoding="utf-8"),
)
page.add_item(css)
book.add_item(page)
spine.append(page)

page2 = epub.EpubHtml(
    title="第2話",
    file_name="2.xhtml",
    lang="ja",
    content=Path("2.xhtml").read_text(encoding="utf-8"),
)
page2.add_item(css)
book.add_item(page2)
spine.append(page2)

book.toc = (
    (epub.Section("第一章　新たなる希望"), (page1, page2)),
)

book.add_item(epub.EpubNcx())
book.add_item(nav)
book.add_item(css)
book.spine = spine
epub.write_epub("sample.epub", book, {})
```

目次やページはこのようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222311/0136dd4e-66a7-a830-4c15-0ec2ba0a4aef.png)

ページとして追加するxhtmlはhtmlと互換性があるため、javascriptは動かないもののWeb小説のhtmlページをそのまま読み込ませることが可能です。


# 縦書きcssの記述
これらを縦書き化するためにはそれぞれのページに縦書き用のCSSを適用する必要があります。
基本的には `writing-mode: vertical-rl;` を指定することで、文字を縦書き・右から左に並べることができます。

```bookstyle.css
html {
    -webkit-writing-mode: vertical-rl;
    -epub-writing-mode: vertical-rl;
    writing-mode: vertical-rl;
}
```

また、ページ送りを右からにするために、`book.direction = "rtl"`を指定する必要があります。

```sample.py
book.direction = "rtl"
epub.write_epub("sample.epub", book, {})
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222311/04dca889-c71c-bd2e-e944-93c4c6ba9abc.png)

早速怪しい算用数字が見えますが、細かいところは次章で扱います。

その他電子書籍でよく使いそうなhtmlタグ・CSSスタイルをまとめます。

* ルビを振るHTMLタグ：`<ruby>彼方<rt>かなた</rt></ruby>`
* 傍線を引くCSS：`text-decoration: overline;`
* 傍点をつけて強調するCSS：`text-emphasis-style: filled dot;`
* イタリック：`font-style: italic;`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222311/2fcf0482-f449-3d2a-c39f-6dde1a29b9d0.png)

# 縦書き化するときの罠一覧
この章では縦書き化するときに私がはまったポイントを紹介します。

## 目次に勝手に数字が付く
そのままの実装だと目次の各タイトルにol,liタグ由来の番号が付いてしまいます。これを除くためには目次用のCSSを用意してNavページに適用する必要があります。

```toc.css
ol li {
    list-style: none; 
    -webkit-text-orientation: mixed;
    -epub-text-orientation: mixed;
    text-orientation: mixed;
}
```

## 算用数字・英字が90度回転する&長い英文どうするか問題
現在の設定では算用数字や英字が90度回転しており、自然な表示にするためには `text-orientation: upright;`を指定する必要があります(デフォルトは`mixed`)。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222311/09ddd831-2fc9-22de-92db-7df90a4403ad.png)

ですが、画像を見るとわかる通り、英字すべてを90度回転させると今度は英文がダサくなってしまいます。そのため、文中で単独で現れる英字 `/([^ -~])([a-zA-Z]{1})([^ -~])/` のみに対して`upright`属性を持つタグで包むなどの処理が必要になります。

また、htmlで記述されたファイルに素朴な正規表現で操作するとhtmlタグにヒットしたりしてしまうため、htmlとして操作するライブラリを使った方がよいでしょう。JQueryのように操作できる[PyQuery](https://github.com/gawel/pyquery)がおすすめです。

## ASCII以外で横書きするものが紛れ込む
紛れ込むものが必ずしも英文字とは限りません。別の言語だったり、特殊な単位だったりが混ざったりするのでなかなか完璧な条件分岐は難しいです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222311/26b09233-06d4-f348-d750-e9a8aa890976.png)


## upright属性では一部の連続文字がおかしくなる
これはKindle Previewerでは観測できず、Paperwhiteに入れて初めて分かる事象です。英字の条件分岐をさぼって全部`upright`でいいやと思っても、ダッシュや三点リーダーなど一部の文字も回転させてしまうことがあります。これはKindle Previewerでは正常に表示されているためいざ読むときに初めて気づくことになります。

![IMG_4909.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222311/0c2c18b2-68e6-5e0d-b1aa-260bc6a177e8.jpeg)

## 3桁程度の数字・英字をまとめる（縦中横）
上で示した「AB型」や「100」、「10」といった小さい数を書くとき、横向きにまとめることでいい感じにすることができます。これを縦中横と呼び、`text-combine-upright: all;`で実現できます。

```bookstyle.css
.combine {
    -webkit-text-orientation: upright;
    -epub-text-orientation: upright;
    text-orientation: upright;
    -webkit-text-combine-upright: all;
    -epub-text-combine-upright: all;
    text-combine-upright: all;
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222311/c2fe7d42-e155-0492-76d5-17550428106e.png)

また、数字をいい感じに表示する方法として縦中横のほかに漢数字に変換するという選択肢もあります。

## 横書きにおける下線は縦書きだと左に付く
割と環境によって変化する闇ポイントの一つです。横書きで使われている下線`text-decoration: underline;`は、縦書きにするとPaperwhiteでは文字の左に付いてしまいます。そのためいわゆる傍線にするときは`text-decoration: overline;`に変更する必要があります。また、明示的に表れる下線タグだけでなく、脚注などのリンクに下線がCSSによって引かれているケースなどにも注意する必要があります。

```bookstyle.css
u {
    text-decoration: overline;
}
```

## 母音に濁点
「あ」に濁点など、文字に濁点文字を連ねる形で表現しているものは縦書き時に壊れてしまいます。このようなケースでも縦中横のようにそこだけ文字を横に並べる必要があります。

参考：https://note.com/774tip/n/n3aa97a259e44

## Web小説サイトが動的にテキストを編集している
たまにウェブサイトがフェッチしてくるHTMLファイルにMarkdown記法が紛れていて、一緒に読み込まれるjavascriptによって動的にそれらをHTMLタグに置き換えていることがあります。こういったものをウェブページの保存時もしくはEPUBへの変換時に注意する必要があります。

# (おまけ) Future work
## Send to Kindle for EmailとWeb連載小説の自動転送
どうやら特定のEmailアドレスにファイルを送信することで[Send to Kindleと同等のことができる](https://www.amazon.co.jp/sendtokindle/email)ようです。これはつまりサーバーを起動しておくことでRSSでWeb連載の最新ポストに追随し、自動でPaperwhiteに転送することができそうです。いつかこれを実装してみたいなーと思っています。

# まとめ
この記事ではWeb小説などHTMLで記述されているファイルを縦書きに変換し、Kindle Paperwhiteに転送する方法を紹介しました。