SATySFiの標準クラスで長いタイトルを無事に出力する（アレな）方法

> これは「[SATySFi Advent Caleandar 2018](https://adventar.org/calendars/3418)」の10日目の記事です。
（9日目は [bd_gfngfnさん](https://github.com/gfngfn/SATySFi/wiki/SATySFi%E3%81%AE%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E5%87%BA%E5%8A%9B%E3%83%A2%E3%83%BC%E3%83%89%E3%81%AE%E3%83%97%E3%83%AD%E3%83%88%E3%82%BF%E3%82%A4%E3%83%97) でした。11日目は [youzさん](http://youz.hatenablog.com/entry/2018/12/11/000000) です。）

## stdjaでタイトルが長くなるとアレ

SATySFiの標準クラス（stdjaクラスなど）においては、タイトル（文書表題）は中央揃えで出力されます。

```satysfi:sample1.saty
@require: stdja

document (|
  title = {今日はマナー違反}; % フツーのタイトル
  author = {ブランチュール非過激派};
  show-title = true; show-toc = false;
|) '<
  +p{意味はまだ無い。}
>
```

![sample1.png](https://qiita-image-store.s3.amazonaws.com/0/14550/a2d0ddb3-56bf-ed73-24c4-3bf9ecdc80d6.png)

ところが、タイトルのテキストが長くなって1行に収まらない場合、中央揃えの結果が**アレ**になってしまいます。

```satysfi:sample2.saty
@require: stdja

document (|
  % うわっ…私のタイトル、長すぎ…？
  title = {オレの\SATySFi;文書のコンパイルが
      一発で通ったから今日はマナー違反};
  author = {ブランチュール非過激派};
  show-title = true; show-toc = false;
|) '<
  +p{意味はまだ無い。}
>
```

![sample2.png](https://qiita-image-store.s3.amazonaws.com/0/14550/6d76ca69-867a-f310-a038-5909f94495fd.png)

## アレ不可避の予感

この不具合の直接の原因は、「タイトルに適用されている中央揃えの実装が複数行のテキストに対応していない」ということです。なぜそんな実装になっているのかというと、その背景には**「中央揃えと自動行分割の両立が難しい」**という理由があります。この辺りの話は[『The SATySFibook』][SATySFibook]の5.3節「行分割を行なうコマンド」において説明されているので、詳しくはそちらを参照してください。要するに、中央揃えを適用する段落においては自動の行分割は困難であるため、少なくとも**「ユーザがあらかじめ行分割した形のテキストを入力に与える」**という仕様にする必要があるわけです。

[SATySFibook]: https://github.com/gfngfn/SATySFi/wiki/The-SATySFi%E2%80%8Bbook-Web%E5%85%AC%E9%96%8B%E7%89%88-%E7%AC%AC1%E7%89%88

そして、「あらかじめ行分割する」という仕様の下では複数行にわたる中央揃えは実現できます。つまり次のようなブロックコマンド`+centerings`は実装可能です。（実装コードは『The SATySFibook』を参照。）

```text
% +centerings : [inline-text list] inline-cmd
% 引数のリストの各々の行を中央添揃えにしたものを行送り方向に
% 並べた段落を出力する.
+centerings{
| オレの\SATySFi;文書の
| コンパイルが一発で通ったから
| 今日はマナー違反
|}

% {|ほげ|ふが|ぴよ|} は [{ほげ}; {ふが}; {ぴよ}] の構文糖衣で
% inline-text list 型をもつ.
```

それなら、タイトルの出力についても同じ実装に変えればよい、と思うかもしれませんが、話はそう簡単ではありません。現在のstdjaのタイトル部分（`document`引数の`title`ラベルの値）はinline-text型ですが、新しい実装を使うとinline-text list型に変わります。つまり、文書ソースのレベルで互換性が失われてしまいます。

```text
  % +centerings と同様の実装を導入するとなると,
  % 常にinline-text listを指定する必要がある.
  title = {|今日はマナー違反|};
```

将来に仕様変更が行われる可能性はありますが、いずれにしても、現状の実装においては、複数行のタイトルを正常に出力する真っ当な策はなさそうです。

## 無慈悲なバッドノウハウ

仕方がないので、ここは**バッドノウハウに訴える**ことにしましょう。次のような手順を使います。

  * 先述の考えと同じく、ユーザがあらかじめめテキストの行分割位置を決めておく。
  * pervasivesパッケージ（標準配布）を読み込む。
  * タイトルのインラインテキストを書く際に、分割後の各行を`\no-break`コマンドで包む。
  * さらに、分割位置に`\fil-both`コマンドを挿入する。

この手順に従ってsample2.satyを書き直すと以下のようになります。

```satysfi:sample3.saty
@require: stdja
%↓これを追加
@require: pervasives

document (| % \fil-both;の直後の改行文字は % で無効化しておく
  title = {\no-break{オレの\SATySFi;文書の}\fil-both;%
           \no-break{コンパイルが一発で通ったから}\fil-both;%
           \no-break{今日はマナー違反}};
  author = {ブランチュール非過激派};
  show-title = true; show-toc = false;
|) '<
  +p{意味はまだ無い。}
>
```

![sample3.png](https://qiita-image-store.s3.amazonaws.com/0/14550/09a14847-0191-2ac4-824b-fe71d60d25d5.png)

マトモな出力になりました！

## 補足

実はここで紹介した方法には元ネタがあって、それはSATySFiのドキュメントの一つである [doc/doc-primitives.saty][primitives] のタイトルです。

![image11.png](https://qiita-image-store.s3.amazonaws.com/0/14550/cf647bbc-b278-69df-38fe-f9eb837b7db4.png)

gfn氏も使ってるんだから問題なし！（多分）

[primitives]: https://github.com/gfngfn/SATySFi/blob/master/doc/doc-primitives.saty