Re:VIEW Starterの新機能（2019年冬）

## はじめに

[Re:VIEW Starter](https://kauplan.org/reviewstarter/)とは、技術同人誌を作成するのに広く使われているRe:VIEWを大幅に改善・拡張したツールです。オリジナルのRe:VIEWと比べて、Re:VIEW Starterは「かんたん」「きれい」「はやい」という特徴があります。また`0`と`O`、`1`と`l`の見分けがつきやすいフォントを選ぶなど、細かいところに気が利いています。詳しくは次の記事を参照してください。

* [技術系同人誌を書く人の味方「Re:VIEW Starter」の紹介](https://qiita.com/kauplan/items/d01e6e39a05be0b908a1)
* [Re:VIEW Starterの新機能（2019年夏）](https://qiita.com/kauplan/items/dd8dd0f4a6e0eb539a98)

この記事では2019年冬時点での新機能を紹介します。

なお「Re:VIEW Starter」はちょっと長いので、以降では「Starter」と呼びます。


## インライン命令の入れ子に対応

ついに、インライン命令の入れ子ができるようになりました。
たとえば `@<strong>{@<LaTeX>{}}` や `@<code>$func(@<i>{arg})$` のような書き方ができます。

```
 * サンプル @<strong>{サンプル @<LaTeX>{}}
 * @<code>$func(arg1, @<i>{arg2})$
```

<img width="663" alt="starter1-b.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224266/8eb838aa-867a-c5fd-15a1-0835f7eb3537.png">

ただし `@<m>{...}` と `@<raw>{...}` と `@<embed>{...}` は対象外であり、入れ子のインライン命令を取れません。

またインライン命令の入れ子をしたくないときは、たとえば `@<strong>$@<nop>{@}<TeX>{}$` のようにしてください（`@<nop>` は引数をそのまま表示する機能）。

```
 * @<strong>$@<nop>{@}<TeX>{}$
```

<img width="611" alt="starter2-b.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224266/7e081d1f-45aa-5b54-5ece-1dd8c9bb0965.png">

#### インライン命令を入れ子にできないことによる問題点

さて、今まではインライン命令の入れ子ができなかったので、たとえばコードの一部を太字にしたい場合、Re:VIEWでは「太字のコード」を表す `@<ttb>{...}` を使って次のようにします。

```
@<code>{git merge }@<ttb>{branch-name}@<code>{ -m }@<ttb>{"message"}
```

すぐに分かると思いますが、この方法だと入れ子にしたい命令の組み合わせをすべて専用命令として定義する必要があります。入れ子にできればこのようなことは必要ありませんが、入れ子にできないとこんな無理やりな方法になってしまいます。

またこれをHTMLに変換すると、次のようになります（読みやすいようインデントを追加しています）。

```html
<p>
  <code>git merge </code>
  <code class="tt"><b>branch-name</b></code>
  <code> -m </code>
  <code class="tt"><b>&quot;message&quot;</b></code>
</p>
```

そして `<code>` に背景色をつけるよう、次のようなCSSを使ってみます。

```css
code {
  background: #eee;
  padding: 2px 2px;
  margin: 0 2px;
  border-radius: 2px;
}
```

すると次のような、不自然な表示になってしまいます。

<img width="479" alt="starter9-b.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224266/df1ccb27-8978-0215-9394-e1bea57b6929.png">

なぜこうなるかというと、本来なら1つの `<code>` タグになるはずのものが、複数の `<code>` タグになってしまっているからです。
そしてインライン命令が入れ子にできれば、1つの `<code>` タグにできるのでこのような問題は起こりません。

#### インライン命令の入れ子対応が遅れた理由

このインライン命令の入れ子機能は長いこと切望されていましたが、実現が遅れていました。技術的には別に難しいことはなく、初歩的な構文解析の知識で実現できます。にも関わらず遅れたのは、インライン命令の細かい仕様を把握するために[Re:VIEWのソースコード](https://github.com/kmuto/review/blob/master/lib/review/compiler.rb)を読んでも、理解が難しかったからです。

たとえば皆さんは、インライン命令の書き方によって「'\'」でのエスケープができたりできなかったりすることを知ってましたか？

```
 * @<b>{\}}
 * @<b>$\$$
 * @<b>|\||
```

これをPDFに変換するとこうなります。つまり最初の書き方だと「`\`」でのエスケープが効くけど、2番目と3番目の書き方では効きません。

<img width="588" alt="starter3-b.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224266/1a994140-c97d-88b0-efe1-cd85a0204415.png">

このような細かい仕様を把握するためにソースコードを読むのですが…他人が書いたソースコードは理解が難しいですね。仕方ないので諦めて、外部から分かる範囲でRe:VIEWの仕様を再現するようにしました。
なのでRe:VIEWとの仕様に違いがあるかもしれません。もし違いを見つけたらお手数ですがお知らせください。


## LaTeXコンパイルを高速化

`rake pdf` を実行すると、今まではLaTeXのコンパイルが3回行われていました。これはRe:VIEWでPDF生成が遅い原因のひとつでした。

そこてStarterでは、LaTeXのコンパイルが1回か2回で済むように改良しました。このおかげでPDF生成が大きく高速化しました。特に原稿を少し修正してPDFで確認、少し修正してPDFで確認、と繰り返すときは高速化を実感できます。

そしてこの改善を実現するために、`ReVIEW::PDFMaker` クラスを全面的に書き換えました。もとのクラスは[様々な責務が詰め込まれた大きなクラス](https://github.com/kmuto/review/blob/master/lib/review/pdfmaker.rb)でした。

* 設定ファイル `config.yml` の読み込みと解析
* コマンドラインオプションの解析
* `layout.tex.erb` をレンダリングしてLaTeXファイルを生成
* LaTeXファイルをコンパイルしてPDFを生成

Starterではこれらを別々のクラスに分離し、コードを整理しました。そのおかげでLaTeXのコンパイルやPDF生成を改善できるようになりました。整理されたコードは新機能を追加しやすいですね。


## 特定の章だけをコンパイル可能

環境変数 `$STARTER_CHAPTER` により、コンパイル対象となる章を指定できるようになりました。これを設定すると全体をコンパイルする必要がないので、コンパイルが短時間で済みます。

例：foobar.re だけコンパイルする。

```console
bash$ export STARTER_CHAPTER=foobar
bash$ rake pdf
```

この場合、目次や大扉や奥付は表示されません。これは仕様です。

全体をコンパイルしたくなったら、環境変数をクリアしてください。

```console
bash$ unset STARTER_CHAPTER
```

この機能は、ページ数の多い本を書くときにはとても重宝します。また複数人で執筆する合同誌の場合も、自分が担当する章だけをコンパイル＆確認すればいいので役に立つでしょう。

なおこの機能が実現できたのも、`ReVIEW::PDFMaker` クラスを全面的に書き換えたおかげです。


## 原稿ファイルを置くディレクトリを指定

原稿ファイル（`*.re`）を置くディレクトリを指定できるようになりました。合同誌などで多数の原稿ファイルが存在する場合、いままではカレントディレクトリに散らかっていましたが、それを1か所にまとめられます。

やり方は簡単で、プロジェクト作成時に「原稿ファイルを contents ディレクトリにまとめる」というチェックボックスがあるのでチェックを入れてください。

<img width="762" alt="starter4-b.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224266/5a264622-c83c-bdca-7f88-7137af441c96.png">

または config.yml に `contentdir: contents` と指定しても同じことができます。

ただしこの機能を使った場合、カレントディレクトリに原稿ファイルがあるとエラーになるので注意してください。またこの機能はRe:VIEW3と同じですが、仕組みは違うので注意してください。

なおこの機能が実現できたのも、`ReVIEW::PDFMaker` クラスを全面的に書き換えたおかげです。


## HTMLファイル生成を改善

* `rake web` でhtmlページに変換する機能を強化しました。PDF生成と比べて非常に高速なので、原稿執筆中の確認方法として `rake pdf` のかわりに使ってください。またhtmlのデザインもなるべくPDFに似せています。
* `rake web` 用に新しいCSSファイル `css/webstyle.css` と `css/normalize.css` を用意しました。今まではepub用のCSSファイルを使ってましたが、専用のCSSファイルを用意したので、デザインをカスタマイズしやすくなります。
* `rake web` 用のレイアウトファイル `layouts/layout.html5.erb` を用意しました。ePub用とは別なので、ePubに影響を与えることなくhtmlをカスタマイズできます。
* `rake web` 用の設定を config.yml の最後に追加しました。


## コードブロックで取り消し線の自動折り返し

プログラムコードやターミナルの中で取り消し線 `@<del>{...}` を使った場合でも、長い行が自動的に折り返されるようになりました（以前までは `@<del>{...}` を使うと折り返しされませんでした）。

<img width="831" alt="starter5-b.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224266/7c1b75e6-d4eb-c7f5-4084-37be03c3a67f.png">

一見すると地味な機能ですが、この実現には非常に骨が折れました。取り消し線機能を提供しているライブラリはいくつかありますが、折り返しを実現する `\seqsplit` とはどれも相性が悪く、自動折り返しができません。仕方ないのでライブラリのソースを読み比べ、カスタマイズできそうなものを選び、モンキーパッチを当てるハックをすることでなんとか実現できました。

CSSだと簡単なことがLaTeXだととても大変な例です。


## 埋め込みコードに背景色をつける

文章中にコードを埋め込む `@<code>{...}` の背景色を薄いグレーにするオプションを用意しました。プロジェクト作成時に指定するか、またはconfig-starter.ymlにおいて `inlinecode_gray: false` を `true` に変更してください。

<img width="723" alt="starter6-b.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224266/7b7c8674-77ac-dd32-0c4a-3cdda50492a1.png">

表示結果は次のようになります。

<img width="705" alt="starter7-b.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224266/c0bc8e90-63cf-cd54-670e-ec45becee23d.png">


## コード内でのふきだしを実装

Re:VIEW3で追加された `@<balloon>{}` をStarterにも追加しました。これはコード内でのふきだし説明を表します。

```
//list{
function f(n) {
  if (n === 1) {
    return 1;                @<balloon>{初期値}
  } else {
    return n * fib(n-1);     @<balloon>{再帰呼び出し}
  }
}
//}
```

「ふきだし」と言ってますが、Re:VIEWでもふきだしの形はしておらず、単に「←」で表示するだけです。
Starterでは表示を少し改善して、次のようにグレーの文字で表示します。

<img width="579" alt="starter8-b.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224266/56039d31-b0e0-de28-58e6-563e532b1f4d.png">


## 細かい改善点

### PDF関連

* たとえば `rake pdf && open xxx.pdf` を実行すると、従来は実行のたびに新しいウィンドウが開いていましたが、これが同じウィンドウで開くようになりました。これはPDF生成時に「古いPDFを削除して新規作成」ではなく「古いPDFを上書き」するよう変更したおかげです。
  このような変更ができたのは、もちろん `ReVIEW::PDFMaker` を書き直したおかげです。

### インライン命令関連

* Re:VIEW3に合わせて、`\reviewbold` や `\reviewstrong` や `\reviewcode` などのLaTeXマクロを導入しました。カスタマイズがより簡単にできます。
* ルビを振る `@<ruby>{単語, よみ}` をサポートしました。
* 傍点をつける `@<bou>{単語}` をサポートしました。
* リンクを表す `@<href>{url,label}` において、今まではurl中の「,」は「\,」のようにエスケープする必要がありました。これはわかりにくいので、urlとlabelの区切りを半角空白つきの「, 」にした場合はurl中の「,」をエスケープしなくても済むようにしました。
* ルビを表す `@<ruby>{text,yomi}` において、今まではtext中の「,」は「\,」のようにエスケープする必要がありました。これはわかりにくいので、textとyomiの区切りを半角空白つきの「, 」にした場合はtext中の「,」をエスケープしなくても済むようにしました。

### コマンドなど

* <strong>`review-pdfmaker` が使えなくなりました。</strong>理由は `review-pdfmaker` だと新しい `ReVIEW::PDFMaker` クラスを読み込めないからです。かわりに `rake pdf` を使ってください。
* `rake pdf` や `rake epub` を実行すると、必ずコンパイルされるように変更しました。従来はRe:VIEWの仕様により、たとえば原稿ファイルのタイムスタンプが更新されていないけど画像は更新されているような場合に、コンパイルがスキップされていました。
* `rake pdf config=config.yml` のように、rakeタスクでconfigファイル名を指定できるようになりました。これは `review-pdfmaker config.yml` と同じ機能です。
* 原稿ファイルの改行文字が `\r\n` の場合でも動作するようになりました。


## 導入事例

技術書典7や夏コミでは、Re:VIEW Starterで執筆された同人誌が増えました。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">これありがたく使わせていただきます<br>初心者なのでめっちゃ助かります<br><br>ありがとうございますm(_ _)m<br><br> <a href="https://twitter.com/hashtag/reviewstarter?src=hash&amp;ref_src=twsrc%5Etfw">#reviewstarter</a></p>&mdash; ゴリラ@自然に帰りたい (@gorilla0513) <a href="https://twitter.com/gorilla0513/status/1144007280330825728?ref_src=twsrc%5Etfw">June 26, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

<blockquote class="twitter-tweet" data-conversation="none"><p lang="ja" dir="ltr">今回の執筆には、Re:VIEW Starter を利用しました。版組の細かい点を心配することなく本⽂の執筆に専念することができたおかげで、初⼼者ながらもなんとか完成させることができました。ありがとうございます！ <a href="https://twitter.com/hashtag/reviewstarter?src=hash&amp;ref_src=twsrc%5Etfw">#reviewstarter</a> &gt;&gt; Re:VIEW Starterの新機能（2019年夏） - Qiita <a href="https://t.co/P0k1AGWYAF">https://t.co/P0k1AGWYAF</a></p>&mdash; 文 (@ipom) <a href="https://twitter.com/ipom/status/1175371169043628032?ref_src=twsrc%5Etfw">September 21, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">本文は Re:View Starter で書きました。<br>これに出会ってなかったら多分心が折れてた。ありがとうございます。<a href="https://twitter.com/hashtag/reviewstarter?src=hash&amp;ref_src=twsrc%5Etfw">#reviewstarter</a></p>&mdash; pirosap (@pirosap) <a href="https://twitter.com/pirosap/status/1152248621716271105?ref_src=twsrc%5Etfw">July 19, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">今回の <a href="https://twitter.com/hashtag/%E6%8A%80%E8%A1%93%E6%9B%B8%E5%85%B8?src=hash&amp;ref_src=twsrc%5Etfw">#技術書典</a> で <a href="https://twitter.com/_kauplan?ref_src=twsrc%5Etfw">@_kauplan</a> さんのRe:view starter を利用させていただき感謝しています。<br>LaTeXの複雑さをラップして使い易さに特化したので、文書を書くことに集中できました。<br><br>疑問はRe:view v.3でstarter(v.2)の *.reファイルを読込後は互換のないタグの置換だけで文書引継可能でしょうか？</p>&mdash; Pierre@Alexaスキル超入門書発売中 (@npand) <a href="https://twitter.com/npand/status/1178816780858298369?ref_src=twsrc%5Etfw">September 30, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">どれもなんとなく「こうなったらいいな」と思っていたことがどストライクに実現されていて「顧客のニーズを読み取る天才だ...」と思うと同時に、サポートの手厚さに感動しました...<br>今度の <a href="https://twitter.com/hashtag/%E6%8A%80%E8%A1%93%E6%9B%B8%E5%85%B8?src=hash&amp;ref_src=twsrc%5Etfw">#技術書典</a> もRe:VIEW Starter を使わせていただく💪 <a href="https://t.co/pCzB4EIALF">https://t.co/pCzB4EIALF</a></p>&mdash; 大将 (@T_taisyou) <a href="https://twitter.com/T_taisyou/status/1190044628117934080?ref_src=twsrc%5Etfw">October 31, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">報告遅くなりましたが、C97も申し込みました！技書博・C97で新刊「Mastodonから始めるFediverse探訪」という技術同人誌を出します。今度は一人で、EPUBファイルもちゃんと出力できるように頑張っていきたいと思います。C96同様、Re:VIEW Starterの御力を拝借します。 <a href="https://t.co/utYrTxJGkp">pic.twitter.com/utYrTxJGkp</a></p>&mdash; サークル「Blowing」@C97火-南リ30a (@circleblowing) <a href="https://twitter.com/circleblowing/status/1165890125345091584?ref_src=twsrc%5Etfw">August 26, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">カウプランさんのRe:VIEW starter kit使ってる</p>&mdash; さわら (@hiroga_cc) <a href="https://twitter.com/hiroga_cc/status/1149952054552485889?ref_src=twsrc%5Etfw">July 13, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

ご利用報告、ありがとうございます。


## まとめ

今後ともRe:VIEW Starterをよろしくお願いします。

* Re:VIEW Starter ([htttps://kauplan.org/reviewstarter/](htttps://kauplan.org/reviewstarter/))

このほか、PDFにノンブルをつける「PDF Operation」というサービスも作っています。ノンブルは印刷所に入稿するとき必要なので、このサービスでノンブルをつけるといいでしょう。

* PDF Operation ([https://kauplan.org/pdfoperation/](https://kauplan.org/pdfoperation/))


## 関連記事

こんな記事も書いてるので、よければ見てください。

* [PDFにノンブルをつける「PDFOperation」](https://qiita.com/kauplan/items/cf17e8d8446d70b5e973) (Qiita)
* [はじめての表紙デザイン（技術系同人誌向け）](https://qiita.com/kauplan/items/2b2f1acbd8efe76c490b) (Qiita)
* [Re:VIEW 2.5での改善点と残っている問題点](https://qiita.com/kauplan/items/5068e2a0da7847c2014e) (Qiita)