Asciidoctor で日本語横書きの資料を作った話

# これは何？

Asciidoctor で日本語横書きの資料を作った時の話。

# 選定

テキストファイルをソースコードとして、PDFをビルドしたいと思った。
多少PNGなんかの図があったりもするが、ほとんどテキスト。
わりと表がたくさんある。表内の表とか、セルの結合とかたくさんある。
ビルドしたPDFはそのまま印刷屋さんに持ち込んで、紙になる。

Re:VIEW を使うか AsciiDoc を使うか迷った。
Re:VIEW の方が全体的には使いやすそうだし、紙にするという文脈にマッチする気がして良かったんだけど、複雑な表を入れるという機能が無いようなので、仕方なく AsciiDoc にした。

処理系には ruby が好きなので Asciidoctor を選んだ。

# 困ること

しかし、問題点がいくつかあった。

## 日本語の禁則処理

日本語の禁則処理がちゃんとできない。
[asciidoctor-pdf-linewrap-ja](https://rubygems.org/gems/asciidoctor-pdf-linewrap-ja) を入れると非常によくなるが、残る問題もいくつかあった。

例えば、幅の狭いセルの中に英字日本語混じりの文を書くと行頭に不要な空白が入ってしまう場合がある。
それと、そういう文書を作りたいわけではないので困らないんだけど、image 指令のように見えるがそうではないものを作ると、そこでは禁則処理が無効になるという問題もあった。

## 非日本語の禁則

例えば空白を挟まずに `(foo)(bar)` のような文字列で、「`(foo`」+改行+「`)(bar)`」となったり「`(foo)(`」+改行+「`bar)`」となってしまう場合があった。

「`(foo)`」+改行+「`(bar)`」 となってほしい。

## その他

AsciiDoc の文法の範囲内ではできないレイアウトをしたいことがあったりもした。

それと。PDF のしおりを自動生成してくれるのは大変ありがたいと思うんだけど、しおりの先頭が表紙なのはちょっとやだなと思った。

# 施策

仕方ないので、 [asciidoctor-nabetani](https://rubygems.org/gems/asciidoctor-nabetani) という gem を作った。

## 作戦

禁則処理は、asciidoctor 側でゼロ幅空白を入れるのではなく、 prawn 側のレイアウトロジックに手を入れるという作戦にした。
ついては、 `Prawn::Text::Formatted::LineWrap` 辺りを書き換えた。

### 気づいたこと

禁則処理難しい。

「しかし……」の、「しかし」と「……」の間に改行していいかどうか。「…」と「…」の間はどうか。
「三ヶ月」の、「三」と「ヶ月」の間はどうか。
元私立恵比寿中学の「ぁぃぁぃ」さんの名前の途中で改行していいかどうか。
「メメクラゲ」の「メ」と「メクラゲ」の間は改行していいけど「××クラゲ」の「×」と「×クラゲ」の間はどうだろう。

しかし日本語はマシだと思う。
英語等の改行処理を真剣にやるのはとてもつらそうだ。少なくとも辞書をもつ必要があり。しかも単語によってはその単語の品詞を特定しないと改行位置がわからないらしい。さらに、改行で区切ると綴が変わることもあるらしい。
詳しくは [Wikipedia - ハイフネーションアルゴリズム](https://ja.wikipedia.org/wiki/%E3%83%8F%E3%82%A4%E3%83%95%E3%83%8D%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0) と [Wikipedia - ソフトハイフン]( https://ja.wikipedia.org/wiki/%E3%82%BD%E3%83%95%E3%83%88%E3%83%8F%E3%82%A4%E3%83%95%E3%83%B3 )を参照のこと。

ハングル、タイ文字等の禁則処理はどうなってるんだろうと思ったりもしたけど、使う予定がないので対応は全くしていない。

## 効果

こんなふうに改善する。

下表の cjk は `asciidoctor-pdf -r asciidoctor-pdf-cjk -r asciidoctor-pdf-cjk-kai_gen_gothic`
linewrap-ja は `asciidoctor-pdf -r asciidoctor-pdf-linewrap-ja -r asciidoctor/pdf/cjk/kai_gen_gothic/theme_loader`
nabetani は `asciidoctor-pdf -r asciidoctor/nabetani`
として作ったもの。

||cjk|linewrap-ja|nabetani|
|:--|:--:|:--:|:--:|
|所謂半角括弧|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6854/469b45aa-e88b-8df5-629a-a3eacd661d10.png)|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6854/065e7d30-cda6-90b9-df5b-039823c609c7.png)|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6854/b4c61870-46f9-8238-1c04-8d27a88185d7.png)|
|日本語英字混じり|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6854/06051cce-4e2c-2157-2b8b-8f44e860d7d5.png)|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6854/d62f9429-4d3a-28ed-fb8c-bac62749d207.png)|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6854/b8b65c6f-c4c0-b086-aa72-6a8365c89324.png)|
|ニセimage指令|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6854/fda2b8f5-8924-326a-bde7-4340917834d3.png)|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6854/32604b57-e401-9683-bb9e-7ff6650ffd84.png)|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6854/61bedf2e-573e-3a23-10e3-66e9864398ff.png)|

# 残課題

## 改ページ制御

表のキャプションと表の間、図のキャプションと図の間に改ページが入ってしまう場合がある。

上記の2ケースでは改ページを禁止したい。
でもどこを書き換えればいいのかまだわかっていない。

残課題の中ではこれが一番切実。

## アップデート対応

実装の中に手を入れる感じで対応してしまったので、prawn や asciidoctor、asciidoctor-pdf にアップデートが来たら面倒なことになる。

Gemfile があるので動かなくなるわけではないけど、作業が発生するので心配。

## 表と改ページ

複数ページにまたがる表を作ることができるのはありがたい。
でも、セルの結合があると改ページのタイミングを間違ってしまう場合がある。

セル結合がある表がページをまたぐときのページ切り替えは話自体が難しいのでまあ仕方ないかなと思っている。

## フォント

ヒラギノで作りたいけど、 TTF しか対応していないのでヒラギノが使えない。残念。
これは prawn の制限だと思う。ちがうかな。
対応するのはたいへん難しそうだと思う。

# 最後に

[asciidoctor-nabetani](https://rubygems.org/gems/asciidoctor-nabetani) でしのぐことはでき、ほっとしている。

必要な機能はだいたいそろったし、出来上がった PDF もちゃんとしている感じだった。

もっといいソリューション・ライブラリ・ツールはないのかなとも思ったけど、見つけられなかった。あるのかなぁ。