LuaTeXの数式用追加プリミティブについて

:::note info
これは「[TeX & LaTeX Advent Caleandar 2024](https://adventar.org/calendars/10647)」の17日目の記事です。
（16日目は[やまいも](https://yamaimo.hatenablog.jp/)さんの『[行分割アルゴリズムの話。](https://yamaimo.hatenablog.jp/entry/2024/12/16/220000)』、18日目は[zr_tex8r](https://qiita.com/zr_tex8r)さんです。）
:::

## はじめに
昨今、pTeXの実体がupTeXになったり、XeTeXを元にした新たな日本語用エンジンであるnpTeXの開発が始まったりと日本語の(La)TeX事情は着実に変化しつつあります。一方世界に目を向ければ事実上LaTeX3への移行が始まり、アクセシビリティへの対応のためにXeTeXがほぼ見放され、pdfTeXですら将来的に非推奨になりそうな気配があります。[^1]
本記事は、昨今のTeX開発で起こっている変化から最も遠いところのエンジンであるLuaTeXにおける数学用の追加プリミティブについての記事です。

:::note warn
本記事では、LuaTeXで数式を扱う利用者にとっての実質的な最小構成となっているであろうLua**La**TeX+unicode-mathの利用を前提として話を進めます。[^2]ただ、ここで紹介するのがプリミティブである以上、LaTeX文書中でそのまま使うのはあまり行儀が良くないので、プリアンブルなどで適当にラップするなどしましょう。
:::

### 数式用追加プリミティブとは
(La)TeXの一部のコマンド（マクロ）は、エンジンによって定義されているものがあり、それらは一般にプリミティブと呼ばれます。プリミティブと一口に言っても、初めから定義されていたもの、時代とともに追加されていったもの、そして各エンジンが独自に定義したものなど様々なものがあります。
その中でも今回取り上げるのは、LuaTeXで独自に追加されたプリミティブのうち、特に数式用の機能を持つプリミティブ（のごく一部）です。紹介順やグループ分けはかなり適当なので、改善案があればぜひお知らせください。
より詳しいことが知りたければLuaTeXマニュアルを参照してください。[^3]

### とりあえずの結論
- [innerscript](https://www.ctan.org/pkg/innerscript)を使うとスペーシングがちょっと良くなる
- プリアンブルに`\NewDocumentCommand{\sfrac}{o m m}{{{#2} \Uskewed / \IfValueT{#1}{#1} {#3}}}`を追加すると`\sfrac{a}{b}`によってa/bの形で分数を書ける
- もともとあったプリミティブを拡張した追加プリミティブは大抵LaTeX内部で適切に使われている（≒実際に目にすることはない）
- LuaTeXのマニュアルにない情報はLuaMetaTeXかConTeXtのマニュアルに書いてある

## 文書作成者も使えそうなもの
### Uleft / Umiddle / Uright
それぞれ初めの`U`を除いたコマンドの拡張です。[^4]
`height`、`depth`、`axis`、`noaxis`、`exact`、`class`の6つのオプションを受け付けますが、意味のわかりやすい最初の2つ以外を使う機会は多分ありません。
使用例：
```latex
\Uleft height 40pt depth 15pt \{
```

### Umathaccent
基本的にはunicode-mathがよしなにラップしているので気にする必要はありませんが、フォント独自の特殊なアクセントなどが存在していて、それを利用したいという稀有な状況であれば使うかもしれません。

|オプション|引数|意味|
|-:|:-|:-|
|`both` `top` `fixed` `overlay`||アクセントの位置|
|`fixed`||アクセントの長さを固定する|
|`fraction`|整数 `n`|アクセントの長さを中身のn/1000倍にする|

使用例：
```latex
\Umathaccent top 0 0 "305 fraction 1000 {ℚ}
```

### Uskewed / Uskewedwithdelim
スラッシュを用いた、a/bの形の分数を作るコマンドです。[^8]
フォントのパラメーターを参照して位置を調整するので、xfracパッケージの`sfrac`コマンドよりも適切な見た目になります。オプションとして`exact`、`axis`が存在します。
筆者は`Uskewed`のラップとして
```latex
\NewDocumentCommand{\sfrac}{o m m}{{{#2} \Uskewed / \IfValueT{#1}{#1} {#3}}}
```
と定義しています。
使用例：
```latex
{ {1} \Uskewedwithdelims / () exact noaxis {2} }
```

### 伸長コマンド
Unicodeに登録のある一部の長い矢印は`longrightarrow`などのコマンドで利用できますが、OpenTypeフォントにはグリフを伸ばす仕組みが存在しているので、フォントによっては可変長の矢印を使うことができます。基本的には
```latex
\Uhextensible width 1cm 0 "2192
```
などとすることで事足ります。グリフを正確に伸ばせなかった場合の位置調整用オプションとして`left`、`middle`、`right`があります。
その他、[mathtools](https://www.ctan.org/pkg/mathtools)に存在する`xlongrightarrow`などに近い効果を持ったプリミティブ
- `Uunderdelimiter`
- `Uoverdelimiter`
- `Udelimiterover`
- `Udeoimiterunder`
が追加されています。ただし、mathtoolsのものとは違って上下同時に文字を入れたり、自動で長さを調節させたりはできないので注意してください。
使用例：
```latex
\mathrel{\Uunderdelimiter width 2.5cm 0 "2192 {\text{A function with a very long name}}}
```
また、`Uhextensible`と同様に`Uvextensible`も存在しますが、ドキュメントへの記載はありません。
使用例：
```latex
\Uvextensible height 2.2em depth 1.5em \Udelimiter 0 0 "222B
```
これらの伸長コマンドや`(U)left`などは、いずれもOpenTypeフォント内のMATHテーブルにしたがってグリフを配置します。したがって、波括弧の突起や`⥲`の`~`などを左右（上下）に偏らせる、といったことはできません。またこのとき、構築に用いられたグリフはそれぞれが元の記号と同様のコードポイントを持つものとして扱われるので、出力されたPDFにおいては例えば`↔↔↔↔↔↔↔↔↔↔↔↔↔↔↔↔↔↔↔↔↔↔↔↔`のような文字情報が記録されています。よって、たとえ1行に収まった数式であろうと単純にコピーして正しい文字列が得られる保証はありません。[^5]

### Uradical / Uroot
それぞれ初めの`U`を除いたコマンドの拡張で、根号を取り扱います。`sqrt`コマンドの定義中に現れる`sqrtsign`が`\Uradical \symoperators "0221A\scan_stop:`で置き換えられているので何もせずにデフォルトで利用されるはずです。使い方は以下の通りですが、初めの2引数は整数値（`"0`など）を取ります。
```latex
\Uradical <フォントファミリー> <根号の記号> <中身>
\Uroot <フォントファミリー> <根号の記号> <次数> <中身>
```

### Ustack
TeXの記法であるために推奨されない`a \over b`による分数の記述などの、どこまでを分子・分母として扱うかがわからないという問題点を解決するコマンドで、
```latex
\Ustack {a \over b}
```
というように使います。[^6]実際にはamsmathがLuaTeX用にはきちんとこちらを使うようになっているので気にしなくても良いです。
`over`などをどうしても使いたい人はこれも使ってください。


### その他
その他文書作成者にも稀に使う機会がありそうなプリミティブです。
|プリミティブ|備考|
|:-|:-|
|crampeddisplaystyle|
|crampedtextstyle|
|crampedscriptstyle|
|crampedscriptscriptstyle|
|Ustartmath|`\(`と同様[^7]|
|Ustopmath|`\)`と同様[^7]|
|Ustartdisplaymath|`\[`と同様[^7]|
|Ustopdisplaymath|`\]`と同様[^7]|
|Usuperscript|`^`のエイリアス|
|Usubscript|`_`のエイリアス|
|Unosuperscript|`^{\textstyle ###}`にあたる？|
|Usubscript|`_{\textstyle ###}`にあたる？|

## 通常の文書作成には使わないもの
追加プリミティブの中には、もちろん内部動作などに影響を及ぼすものも存在します。大抵の人にとって使う機会はまず訪れないでしょうが、一応紹介しておきます。

### Umath<1><2>spacing
<1><2>には数式中の記号種別（ord/binなど）が入り、それらの間にどれだけの空白を挿入するかを規定するパラメーターです。
とりあえず[innerscript](https://www.ctan.org/pkg/innerscript)はこのあたりをいい感じにしてくれます。

<details><summary>一覧</summary>

- Umathordordspacing
- Umathordopspacing
- Umathordbinspacing
- Umathordrelspacing
- Umathordopenspacing
- Umathordclosespacing
- Umathordpunctspacing
- Umathordinnerspacing
- Umathopordspacing
- Umathopopspacing
- Umathopbinspacing
- Umathoprelspacing
- Umathopopenspacing
- Umathopclosespacing
- Umathoppunctspacing
- Umathopinnerspacing
- Umathbinordspacing
- Umathbinopspacing
- Umathbinbinspacing
- Umathbinrelspacing
- Umathbinopenspacing
- Umathbinclosespacing
- Umathbinpunctspacing
- Umathbininnerspacing
- Umathrelordspacing
- Umathrelopspacing
- Umathrelbinspacing
- Umathrelrelspacing
- Umathrelopenspacing
- Umathrelclosespacing
- Umathrelpunctspacing
- Umathrelinnerspacing
- Umathopenordspacing
- Umathopenopspacing
- Umathopenbinspacing
- Umathopenrelspacing
- Umathopenopenspacing
- Umathopenclosespacing
- Umathopenpunctspacing
- Umathopeninnerspacing
- Umathcloseordspacing
- Umathcloseopspacing
- Umathclosebinspacing
- Umathcloserelspacing
- Umathcloseopenspacing
- Umathcloseclosespacing
- Umathclosepunctspacing
- Umathcloseinnerspacing
- Umathpunctordspacing
- Umathpunctopspacing
- Umathpunctbinspacing
- Umathpunctrelspacing
- Umathpunctopenspacing
- Umathpunctclosespacing
- Umathpunctpunctspacing
- Umathpunctinnerspacing
- Umathinnerordspacing
- Umathinneropspacing
- Umathinnerbinspacing
- Umathinnerrelspacing
- Umathinneropenspacing
- Umathinnerclosespacing
- Umathinnerpunctspacing
- Umathinnerinnerspacing
</details>

### フォント依存のパラメーター
LuaTeXではOpenTypeフォントに元から対応していて、さらにunicode-mathを利用することによりOpenType Mathをフル活用できるようになります。ではOpenTypeフォント内のMATHテーブルの情報をどう扱っているのか、フォント（の中身）に興味のある方なら一度は考えたことがあるでしょう。
実は、LuaTeXではOpenTypeフォントの活用のために数式組版処理が見直されていて、多くのパラメーターがハードコーディングではなくなりました。その中にはフォントの情報を用いて自動設定されるパラメーターも多くあります。OpenTypeフォントでなくともTFMから多くのパラメーターが自動で設定されるので、普通に生きていればまず使うことはないでしょう。

<details><summary>一覧</summary>

- Umathquad
- Umathaxis
- Umathoperatorsize 
- Umathoverbarkern
- Umathoverbarrule
- Umathoverbarvgap
- Umathunderbarkern
- Umathunderbarrule
- Umathunderbarvgap
- Umathradicalkern
- Umathradicalrule
- Umathradicalvgap
- Umathradicaldegreebefore
- Umathradicaldegreeafter
- Umathradicaldegreeraise
- Umathstackvgap
- Umathstacknumup
- Umathstackdenomdown
- Umathfractionrule
- Umathfractionnumvgap
- Umathfractionnumup
- Umathfractiondenomvgap
- Umathfractiondenomdown
- Umathfractiondelsize
- Umathskewedfractionvgap
- Umathskewedfractionhgap
- Umathlimitabovevgap
- Umathlimitabovebgap
- Umathlimitabovekern
- Umathlimitbelowvgap
- Umathlimitbelowbgap
- Umathlimitbelowkern
- Umathnolimitsubfactor
- Umathnolimitsupfactor
- Umathunderdelimitervgap
- Umathunderdelimiterbgap
- Umathoverdelimitervgap
- Umathoverdelimiterbgap
- Umathsubshiftdrop
- Umathsupshiftdrop
- Umathsubshiftdown
- Umathsubsupshiftdown
- Umathsubtopmax
- Umathsupshiftup
- Umathsupbottommin
- Umathsupsubbottommax
- Umathsubsupvgap
- Umathspaceafterscript
- Umathconnectoroverlapmin
</details>

### TeXのプリミティブを拡張するもの
LuaTeXではTeXと比較してレジスタが大幅に拡張され、さらにコードポイントとしてUnicode全体を扱うようになっています。この仕様を後方互換性を維持したまま実現するために、いくつかのプリミティブが拡張されて語頭に`U`を付加した別名のプリミティブとして追加されています。これらのプリミティブは引数にも若干の変更が加えられていますが、筆者自身がよく理解できていないので詳細はLuaTeXのマニュアルを参照してください。

<details><summary>一覧</summary>

- Umathchardef
- Umathcharnumdef (*, **)
- Umathcode
- Udelcode
- Umathchar
- Umathaccent
- Udelimiter
- Umathcharnum
- Umathcodenum
- Udelcodenum

(*): `mathchardef`に`0x8000`を超える入力がなされた場合は`Umathcharnumdef`として扱われる
</details>

### その他
上で紹介した他にもいくつかのプリミティブが存在します。筆者が理解している範囲でプリミティブの効果を記載しますが、備考に記載のないものや引数などはマニュアルを参照してください。

<details><summary>一覧</summary>

|プリミティブ|備考|
|:-|:-|
|mathsurroundskip|
|mathsurroundmode|`mathsurround`と`mathsurroundskip`の挙動の制御[^9]
|mathscriptboxmode|上（下）付き数式内テキスト周りのカーニングの制御？
|mathscriptcharmode|上（下）付き数式内テキストにおけるカーニングの制御？
|mathrulethicknessmode|*undocumented*
|mathflattenmode|
|prerelpenalty|
|prebinoppenalty|
|mathpenaltiesmode|
|mathdelimitersmode|
|variablefam|
|predisplaygapfactor|別行立て数式前の空白（em単位）
|mathdefaultsmode|
|Udelimiter|
|mathstyle|
|Umathcharclass|
|Umathcharfam|
|Umathcharslot|
|mathdir|
|mathoption|デバッグ用？
|mathdirection|
|suppressmathparerror|
|matheqnogapstep|数式と番号の間の空白
|matheqnogapstep|
|mathdisplayskipmode|
|mathscriptsmode|
|mathnolimitsmode|
|mathitalicsmode|
|mathrulesmode|
|matheqdirmode|
|mathrulesfam|*undocumented*

</details>

## おわりに
本記事では各プリミティブについて詳しい仕様に踏み込んだ解説をすることは叶わず、一覧を挙げてその効果の概略を記すことしかできませんでした。LuaTeXの追加プリミティブについてさらに知りたければLuaTeXのマニュアルを見れば良い……と言い切れればよかったのですが、実際にはLuaTeXのマニュアルには以下のようにいくつかの注意点があります。

- 一部のプリミティブについて説明が記載されていない
- 組版結果の違いのサンプルがあるべき場所で、正しく異なる組版結果が出ていない場合がある
- 一部のサンプルコードがConTeXt用になっている

これらの要因から、LuaTeXについて知りたい場合でもLuaMetaTeX[^10]のマニュアルを見るか、ConTeXtの[プリミティブマニュアル](https://www.pragma-ade.nl/general/manuals/primitives.pdf)あるいは[低レベル組版マニュアル](https://www.pragma-ade.nl/general/manuals/lowlevel.pdf)を見る必要があるなど、その調査は困難を極めます。[^11]
余談ですが、LuaTeXのソースコードを覗いてみると、C言語のコードに混じってコメントアウトされた解説がTeXで書かれています。筆者はこれをタイプセットした文書をついに見つけられなかったのですが、この解説を文書化したもの、あるいは文書化する方法をご存知の方がいらっしゃればご連絡ください。

Lua(La)TeXと日本語TeXのさらなる発展を祈ります。皆様も良きTeXライフを。

[^1]: XeTeXが見放されるのは開発の停滞も大きな要因になっているようですが、npTeXがリリースされた暁にはアクセシビリティ対応の責を追うことになるのでしょうか？
[^2]: この要件を満たさない場合に記事の内容が全く役に立たないことを意味しません。
[^3]: いくつかドキュメントに記載のないプリミティブも存在しますが……
[^4]: 何故かLuaTeX中では`luatex`ではなく`tex`コマンドとして登録されています。もしかしてpdfTeXにもある？
[^8]: タグ付けは部分的に対応しているようで、`mml`ファイル上では`bevelled="true" linethickness="0"`の`mfrac`として、すなわち線の幅が0の傾いた分数として出力されます。（[xfrac](https://www.ctan.org/pkg/xfrac)の`sfrac`を利用すると数式内テキスト内数式で直接`a/b`と入力したかのような出力になります）
[^5]: タグ付きPDFを生成する際に自動生成されるmmlファイルにおいては（横方向について`stretchy="false"`とされて伸長の情報が欠けてしまいますが）きちんと1字だけになっています。
[^6]: `over`の他に`atop`、`overwithdelims`、`atopowithdelims`とも利用できるようです。`Uskewed`等については未検証です。
[^7]: 細かいことをいえばエラーの出る場面などが異なる他、正常なタグ付けを行うことができません。
[^9]: 執筆時現在のマニュアル（LuaTeX 1.18 / 2024年2月版）では表の組版結果が正しくありませんが、LuaMetaTeXのマニュアル（LuaMetaTeX 2.11.02 / 2024年2月版）では正しく出力されています。
[^10]: LuaTeXと並行して開発が進む派生版で、主にConTeXt向けのエンジンのようです。LuaTeXとは袂を分かつようで、変更点をLuaTeXに逆輸入するといったことは基本的に考えられていないみたいです。
[^11]: もちろん、一部のプリミティブはさらに変更が加えられていたり、あるいは削除されたりしているので、そのままLuaTeXで使えるとは限らず注意が必要です。