マクロツイーター

はてダから移行した記事の表示が崩れてますが、そのうちに直せればいいのに(えっ)

ルビはじめました(7) ― 段落末尾での挙動

これまで段落冒頭での進入の挙動について思案していたが、よく考えると、段落末尾での挙動について全く考えていなかったことに気がついた。なので今考えている。

段落冒頭であることを判断するのは容易であり(出力を行う前に垂直モードであるかを判定する)、実際に判定結果を用いた分岐を行っている。それに対して、段落末尾であることを確実に判定するのは不可能に思える。従って、段落末尾で実行される操作を見越して適切にグルー・カーン・ペナルティを並べておくという対策が必要なようである。

以下では、この適切な配置について考察する。前提条件は以下の通り。

  • TeX の段落終了処理について。段落末尾では以下のトークン列が実行される。
    \unskip\penalty10000 \hskip\parfillskip
    
    この先頭の \unskip は普通は段落末尾にある空白トークンによるグルーを吸収するためにあるのだが、これで「調整用グルー」を消すことができたら、段落末尾か否かで空白の入り方を変えることができそうである。
  • pxrubrica が行っている、ルビの後ろ側の「先読み処理」について。ルビの後ろ側を「後退するグルー」で単純に終わらせておくと、そこの禁則ペナルティの処理が上手くいかない。*1そこで、「先読み処理」を行って、直後に前禁則ペナルティ(\prebreakpenalty)をもつ文字が存在するかを検査している。((ただし、安全のため、「直後に文字そのものがある場合」以外、例えば後続が「}、」とか「『、』に展開されるマクロ」とかの場合は後ろ側の禁則はないと判断される。この状況で行分割を禁止したい場合は * 指定が必要。))そして、存在する場合、前禁則ペナルティと同じ値のペナルティをグルーの前に入れるという対処策を採っている。従って、このペナルティ挿入があるかどうかで構成を分けて考えた方がいいだろう。

以上を踏まえて、次の 2 つの場合に分けて考える。

  • (1) 直後に前禁則ペナルティ p の文字が続く場合。なお、後方行分割禁止(*)を指定した場合は p = 10000 の文字が後続するのと全く同じ扱いを行う。
  • (2) それ以外の場合。段落末に存在し得るのはこの場合のみ。((つまり、* 指定を段落末で行うのは想定外。))

かなり悩んだ結果、段落末尾での進入を許さない場合と許す場合の双方について、希望通りの形を得ることができることが解った。以下の記述では、ルビ付き文字が収まった hbox を「(ブロック)」とし、後ろ側の進入量を a とし、文字間空白として挿入する空白量(ゼロ、\kanjiskip\xkanjiskip のいずれか((和欧文空白挿入(:)指定なら \xkanjiskip、そうでない場合、和文ルビなら \kanjiskip、欧文ルビならゼロ。)))を c とする。

  • [A] 段落末尾での進入を許さない場合:
    • (1): (ブロック)\penalty(p)\hskip(−a + c)\penalty20000
      この末尾に置いたペナルティに「本物の前禁則ペナルティ」の p (≧ −10000)が加算されるが、結果の値は 10000 以上なのでこの位置では行分割されない。p < 10000 なら前の \penalty(p) で分割され得て、その場合、後退するグルー(\hskip)が消滅するので進入が起こらなくなる。
    • (2): (ブロック)\hskip(−a + c)
      この場合、段落末尾では以下の形に変わる:
      (ブロック)\penalty10000\hskip\parfillskip
      後退するグルーが消滅するので進入は起こらない。
  • [B] 段落末尾での進入を許す場合:
    • (1): (ブロック)\penalty(p)\kern(−a)\hskip(c)\penalty20000
      [A] の場合と同じ。
    • (2): (ブロック)\kern(−a)\hskip(c)
      この形のまま(段落中)では、カーンの前で行分割が起こり、その場合、後退するカーンが消滅する。 段落末尾では以下の形に変わる:
      (ブロック)\kern(−a)\penalty10000\hskip\parfillskip
      ここでは、ブロックの後に行分割可能点は存在せず、後退するカーン(進入設定)はそのまま活きる。

というわけで更新。

*1:グルーの後にペナルティが入るので、結局役に立たない。