これは「SATySFi Advent Calendar 2020」の1日目の記事です。
2日目はmatsud224さんによる「Satyrographos Package Index の紹介」です。
とうとう始まりましたSATySFi Advent Calendar 2020。
楽しんでいきましょう!
今回はSATySFiでルビ(振り仮名)付きのテキストを出力する機能を提供するパッケージである、「SATySFi-ruby」を紹介(宣伝)します。
このパッケージは2020/12/01時点でヴァージョンが0.1.2と、まだまだ改善の余地のあるモノではありますが、ルビを表示するという一定の機能を有しています。
また、SATySFiの基本的な機能のみを使っているため、SATySFi for Windowsなどの「古いSATySFi」でも動きます。
satyrographos-repoに既に登録されているため、satyrographosとopamがインストールされている状態で
opam update opam install satysfi-ruby && satyrographos install
とするだけでインストールできます。
SATySFiでルビ出力をするパッケージは今までに存在していないため、「パッケージとして存在する」というだけでアピールポイントにはなりますが、それ以外にも
- 「日本語組版処理の要件」(JLREQ) にある規定にできるだけ沿ったルビの組版の実現を目指す。
- 実際のルビ組版で必要となる空白などの処理をなるべく自動化で実現しているため、ユーザーが手で空白を打ち込んで調整する必要がない。
というポイントがあります。
では、使い方の解説などをしていきます。
パッケージの読み込み
satyrographosでインストールした場合は
@require: ruby/ruby
とするだけで読み込むことができます。
他の方法でインストールした人は、中にあるruby.satyh
への適切なパスを記述してください。
このパッケージが提供するモジュール名はRuby
です。
基本編
ルビを振るためのコマンドとして\ruby
を提供しています。使い方はこのようになります。
\ruby ?:[<オプション>] [<ルビ文字>] {|<親文字>|}
<ルビ文字>
はstring list
で、<親文字>
はinline-text list
です。
先頭のオプションは省略可能です。詳しい使い方は後程説明します。
とりあえず簡単に使ってみる
親文字が1文字の場合は単純にルビと親文字を一つずつ与えるだけです。
あれは\ruby[`たか`]{|鷹|}ではなく\ruby[`うぐいす`]{|鶯|}です。
親文字の上にルビが振られてますね。 ルビの方が親文字よりも長い場合も、前後に適切にスペースが入れられているのが分かると思います。
親文字が2文字以上ある場合は、親文字とルビの入力は適切に区切らないといけません。
\ruby[`こ`; `ばと`]{|小|鳩|} \ruby[`く`; `じゃく`]{|孔|雀|} \ruby[`しち`; `めん`; `ちょう`]{|七|面|鳥|}
きちんとそれぞれの親文字の上で中央揃えになっていることがわかると思います。
これである程度のルビを振ることは出来るようになったと思います。
ルビの種類を変えてみる
実はルビにはいくつかの種類があります。
まずは「モノルビ」です。先ほどから例で見ているように、「親文字1字ごとに対応してルビを配置する」というルビの振り方です。文中に登場する単独の漢字に振りたいときに使われたりします。
つぎに「グループルビ」です。これは、熟字訓などの、ルビを分割することができない場合に使われます。
最後に「熟語ルビ」です。 熟語としてひとまとまりになっているのにルビのせいで親文字の間にスペースが入ってしまうことを避けたいときなどに使われます。
比較として、上の例をモノルビで組んだ時の様子を表示してみます。
スペースがいくつか消えていることがわかるでしょう。
勿論、これらを変更する機能があります。
オプション引数にRuby.mode Ruby.m
などのように入れると実現されます。モノルビのときにはRuby.m
、グループルビの時はRuby.g
、熟語ルビの時はRuby.j
です。
v0.1.2の時点ではモノルビがデフォルトです。
モノルビの場合:
\ruby ?:[Ruby.mode Ruby.m] [`く`; `じゃく`]{|孔|雀|}
グループの場合:
\ruby ?:[Ruby.mode Ruby.g] [`く`; `じゃく`]{|孔|雀|}
熟語ルビの場合:
\ruby ?:[Ruby.mode Ruby.j] [`く`; `じゃく`]{|孔|雀|}
さて、これで使い方は大体わかったと思われます。
ルビの前後への進入を調節する機能などもありますが、具体的にはドキュメントをお読みください。
実装方法(簡単に)
簡単にルビの実装方法を解説します。
実装方法に強い関心のある方などは、実際の実装を見ていただくのが良いと思います。
モノルビの場合
ルビと親文字はリストの形で既に貰っているので、これをそのまま利用します。
最初はルビと親文字をそれぞれ一つずつ組んでinline-boxes
(以下ib)にします。
ここで親文字とそれに対応するルビのibの長さを評価します。
もし親文字の方が長かったらルビ文字を中央揃えになるようにスペースを入れ、ルビ文字の方が長かったら親文字が中央になるようにスペースを入れます。
これを全ての親文字とそれに対応するルビに対して行います。
このままですと「一」のように高さの低い文字があったときにルビの高さが揃わなかったり、変にルビの位置が低くなったりしてしまうので調節します。
まず、親文字の高さを比較して一番大きかった高さを記録します。
次に、context
に含まれているフォントサイズの情報とその記録を比較し、大きい方を記録します。
最後に、記録していた最大の高さと実際の親文字の高さのギャップ分スペースを入れます。
こうして親文字の高さが揃うので、line-stack-bottom
というプリミティブを使って親文字とルビを連結し、さらに連結された親文字達どうしを++
プリミティブを使って横に連結させることで完成です。
グループルビの場合
まずは親文字もルビも連結させた状態でibにします。
次に、出来た親文字のibとルビのibの長さを比較します。
親文字の方が長かった場合、ルビを一文字ずつ分解し、長さの差分を均等に分けたスペースを入れながら再度組みなおします。
あとはline-stack-bottom
を使ってルビと親文字を上下に結合させるだけです。
親文字の方が短かった場合も、差分を基に均等に分けたスペースを入れて再度組みなおし、結合させます。
熟語ルビの場合
JLReqで説明されている方法はとても複雑なので、JIS X 4051に規定されている簡単な方を採用しています。
まず、ルビと親文字をそれぞれ組み、長さを評価します。
全ての親文字について、ルビよりも長さが長かったときはモノルビで組みなおします。
そうではなく、一文字でも親文字の方がルビよりも短かいものがあった場合は、グループルビで組みなおします。
いかがだったでしょうか?
こう書くと結構簡単に実装できたかのように見えますが、進入との兼ね合いなどを考えて実装すると結構複雑になりました。
今後実装したい機能
今後実装・改良したい機能はこのような感じです。 もし、「実装・改良してみたよ!」という方がいらっしゃいましたら、リポジトリにプルリクエストを送っていただけると嬉しいです。
- 進入の調節が面倒なので、改良したい。
- 規定値を変更する機能を提供したい。
- 両側ルビや下側ルビにも対応したい。
今回、自分の作ったsatysfi-rubyパッケージを紹介してみました。使っていただけると嬉しいです。
また、今後多くのSATySFiパッケージが公開され、便利になっていくことが楽しみです。