マクロツイーター

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

メモ:新しいLaTeXのフックシステムを理解したい

LaTeXカーネルの2020/10/01の改版で導入される、新しいフックシステムを理解するためのメモ書き。

フック

  • フック(hook)は「ラベル(label)からコード片(code chunk)へのマップ」と「ルール(rule)の集合」をもつ。
  • まだ作成していないフックにコード片を追加することができる。

※△印の命令ではラベル指定でドット記法が使えない

フック作成・実行

  • \NewHook{<フック名>}:フックを作成する。
  • \NewReversedHook{<フック名>}:逆順フック(reversed hook)を作成する。
    ※逆順フックではコード片の実行順が通常とは逆になる。(後述)
  • \NewMirroredHookPair{<フック名1>}{<フック名2>}:フックの組を作成する。
    ※単に\NewHook{<フック名1>}\NewReversedHook{<フック名2>}と同等。
  • \UseHook{<フック名>}:フックを実行する。
  • \UseOneTimeHook{<フック名>}:フックを“one-time実行”する。すなわち、フックを実行した後、「以後そのフックへコードを追加しようとした場合は代わりに即時に実行する」ようにする。

コード片の追加

※オプション引数<ラベル>の既定値は「既定ラベル」。

  • \AddToHook{<フック名>}[<ラベル>]{<コード>}:フックにコード片を追加する。既に当該のラベルに対するコード片がある場合はそのコード片の末尾に引数のコードを追記する。
  • \RemoveFromHook{<フック名>}[<ラベル>]:フックから当該のラベルに対するコード片を削除する。
  • \AddToHookNext{<フック名>}{<コード>}:フックに“次回用”のコード片を追加する。“次回用”のコード片はそのフックが次に実行されたときにのみ実行される。

既定ラベル

  • ドット記法(dot-syntax):フック名またはラベルの引数の値が「.であるか./で始まる」場合は.は「既定ラベル(default label)」に置き換えられる。
    ※ただし△印の命令のフック名引数ではドット記法が使えない。
  • 「既定ラベル」はパッケージ・クラスの読込中はその名前(\@currname)、それ以外はtop-level
  • \DeclareDefaultHookLabel{<ラベル>}:「既定ラベル」を変更する。
    ※有効範囲は当該ファイルの読込中。

ルール

  • ルールが指定されない場合、コード片の実行順は、通常のフックでは追加された順、逆順フックではその逆順となる。
    ※そのように動作すると記載されている。仕様として保証されているのかは微妙であるが、そうでないと「逆順フック」という概念が意味を成さない気がする。 ※“次回用”のコード片は常に(逆順フックでも)最後に実行される。
  • ルールを指定することでコード片の実行順を変更できる。
  • \DeclareHookRule{<フック名>}{<ラベル1>}{<関係>}{<ラベル2>}:フックにルールを指定する。
    ※特定のラベルの組についてルールは1つしか指定できない。既にルールが指定されている場合は新しく指定したもので置き換えられる。
    有効な<関係>の値は以下の通り:
    • before/<:ラベル1のコードはラベル2のコードより前に実行される。
    • after/>:ラベル1のコードはラベル2のコードより後に実行される。
    • incompatible-warning:ラベル1のコードとラベル2のコードは併存できない。違反した場合は警告を出す。
      ※違反の場合、両方のコードが無効になる。
    • incompatible-error:ラベル1のコードとラベル2のコードは併存できない。違反した場合はエラーを出す。
    • voids:ラベル1のコードが存在する場合はラベル2のコードが無効になる。
    • unrelated:ラベル1のコードとラベル2のコードの実行順序を問わない。(これが既定。)
  • \ClearHookRule{<フック名>}{<ラベル1>}{<ラベル2>}:指定のラベルに対するルールを削除する。
    \DeclareHookRule{<フック名>}{<ラベル1>}{unrelated}{<ラベル2>}と同等。
  • \DeclareDefaultHookRule{<ラベル1>}{<関係>}{<ラベル2>}:全フックに対する既定のルールを指定する。
    ※フックに対して指定されたルールの方が既定のルールより優先する。
    ※プレアンブル専用。

情報取得

  • \IfHookEmptyTF{<フック名>}{<真>}{<偽>}:フックが空である(コード片が全く追加されていない)かどうかのテスト。
  • \IfHookExistsTF{<フック名>}{<真>}{<偽>}:フックが存在する(作成されている)かどうかのテスト。
  • \ShowHook{<フック名>}:フックの現在の状態を端末に出力する。
  • \LogHook{<フック名>}:フックの現在の状態をログに出力する。
  • \DebugHooksOn\DebugHooksOffデバッグ用出力を有効/無効にする。

カーネルが定義するフック

当該のフックに追加する専用の命令がある場合は一緒に紹介する。(ただし、\AddToHook{<フック>}と等価であるとは限らない。)

※ ◇印は“one-time実行”されるフック。
※ ☆印は逆順フック。

環境

※etoolboxパッケージで一部の命令が提供されていたが、カーネルに取り込まれた。(新etoolboxは\begin/\endへのパッチを行わなくなる。)

  • env/環境名/before:環境の実行の直前(グルーピングの外)。
    • \BeforeBeginEnvironment[<ラベル>]{<コード>} ※元々はetoolboxの機能。
  • env/環境名/begin:環境のbegin部実装コードの直前。
    • \AtBeginEnvironment[<ラベル>]{<コード>}
  • env/環境名/end:環境のend部実装コードの直前。
    • \AtEndEnvironment[<ラベル>]{<コード>}
  • env/環境名/after☆:環境の実行の直後(グルーピングの外)。
    • \AfterEndEnvironment[<ラベル>]{<コード>} ※元々はetoolboxの機能。

文書本体開始・終了

※etoolboxパッケージでは独自に文書本体開始・終了時のフックを提供している。最新版のetoolboxが新カーネルで動作する場合は、カーネルで用意されたフックを利用する動作に切り替わる。(つまり\(end)documentへのパッチを行わなくなる。)

※atveryendパッケージでは独自に文書本体終了時のフックを提供している。新カーネルLaTeX上ではatveryendはカーネルで用意されたラッパーに置き換えられる。(ただし\BeforeClearDocumentはサポートされない。)

  • begindocument/before◇:\begin{document}の実行の先頭。
    • \AtEndPreamble[<ラベル>]{<コード>}〈新etoolbox〉
  • begindocument◇:\begin{document}の途中。
    • \AtBeginDocument[<ラベル>]{<コード>}
      ※プレアンブル専用であることは変わらず。
    • \AfterPreamble{<コード>}〈新etoolbox〉
  • begindocument/end◇:\begin{document}の実行の最後。
    • \AfterEndPreamble{<コード>}〈新etoolbox〉
  • enddocument◇:\end{document}の実行の先頭。
    • \AtEndDocument[<ラベル>]{<コード>}
  • enddocument/afterlastpage◇:\end{document}で最終ページを吐き出した直後。
    • \AfterLastShipout{<コード>}〈新atveryend〉
  • enddocument/afteraux◇:\end{document}でauxファイルを再読込した直後。
    • \AtVeryEndDocument{<コード>}〈新atveryend〉
  • enddocument/info◇:enddocument/afterauxフックの直後。
    • \AtEndAfterFileList{<コード>}〈新atveryend〉
  • enddocument/end◇:TeXの実行が終了する直前。
    • \AfterEndDocument{<コード>}〈新etoolbox〉
    • \AtVeryVeryEnd{<コード>}〈新atveryend〉

フォント選択(NFSS)

※詳細未調査

  • rmfamily
  • sffamily
  • ttfamily
  • normalfont
  • expand@font@defaults
  • bfseries/defaults
  • bfseries
  • mdseries/defaults
  • mdseries

ページ出力(shipout)

※新カーネルLaTeX上ではeveryshiはカーネルで用意したラッパーに置き換えられる。

※新カーネルLaTeX上ではatbegshiはカーネルで用意したラッパーに置き換えられる。

※atenddviパッケージの機能はカーネルに取り込まれた。

  • shipout/before:実際にページ出力が起こる前に行う処理。
    ※shipoutのフックの中でこれだけは「実行する内容」をコード片とする。残りのフックは「ボックスに挿入する内容」をコード片とする。
    ※ページ出力予定のボックスの内容は\ShipoutBoxレジスタに入っていて、フックのコードでこの内容を変更することができる。
    • \EveryShipout{<コード>}〈新everyshi〉
    • \AtNextShipout{<コード>}〈新everyshi〉:“次回用”に追加。
    • \AtBeginShipout{<コード>}〈新atbegshi〉
    • \AtBeginShipoutNext{<コード>}〈新atbegshi〉:“次回用”に追加。 ※新atbegshiでは\AtBeginShipoutBox\ShipoutBoxと等価。
  • shipout/background:直接位置指定で各ページの背景に描画する内容。
  • shipout/foreground:直接位置指定で各ページの前景に描画する内容。
    backgroundforegroundのコードはページ左上隅を原点とするpicture環境内で実行される。単純に「全ページに挿入する」という目的でも使える。
    • atbegshiでの類似の機能は\AtBeginShipoutUpperLeft(Foreground)であるが、インタフェースが少し異なる。\AtBeginShipoutUpperLeft(…)はそれ自身はフックでなく\AtBeginShipoutBoxを操作する命令であり、他のatbegshiのフック中で使われる想定である。従って、新atbegshiの実装もフックに帰着されるのではない。
  • shipout/firstpage:最初のページに挿入する内容。
    • \AtBeginDvi{<コード>}
    • AtBeginShipoutFirst{<コード>}〈新atbegshi〉
  • shipout/lastpage:最後のページに挿入する内容。
    ※2回コンパイルが必要。
    • \AtEndDvi{<コード>} ※元々はatenddviの機能。

最終的にページ出力されるボックスの中では、以下の順で内容が配置されている。

  • shipout/firstpageの内容(先頭ページのみ)
  • shipout/backgroundの内容
  • 元の出力ボックスの内容をshipout/beforeで加工したもの
  • shipout/foregroundの内容
  • shipout/lastpageの内容(最終ページのみ)

※1つのフック内では“次回用”のコード片は最後に実行されることに注意。

ファイル読込

filehookパッケージの仕様に従っている。filehookを置き換えるためのラッパーであるfilehook-ltxパッケージが作成されているが、まだ一部の機能が実装されていないため、filehookを置き換えるようにはなっていない。 filehookが読み込まれた場合は、その実装がそのまま使われる。

ファイル名はパス無しで拡張子付の名前。

  • file/before:何かのファイルが読まれる直前。
    • \AtBeginOfEveryFile{<コード>}〈filehook-ltx〉
  • file/before/ファイル名:そのファイルが読まれる直前。
    • \AtBeginOfFile{<ファイル名>}{<コード>}〈filehook-ltx〉
  • file/after☆:何かのファイルが読まれた直後。
    • \AtEndOfEveryFile{<コード>}〈filehook-ltx〉
  • file/after/ファイル名☆:そのファイルが読まれた直後。
    • \AtEndOfFile{<ファイル名>}{<コード>}〈filehook-ltx〉
  • package/before:何かのパッケージが読まれる直前。
    • \AtBeginOfPackages{<コード>}〈filehook-ltx〉
  • package/before/パッケージ名:そのパッケージが読まれる直前。
    • \AtBeginOfPackageFile{<パッケージ名>}{<コード>}〈filehook-ltx〉
  • package/after☆:何かのパッケージが読まれた直後。
    • \AtEndOfPackages{<コード>}〈filehook-ltx〉
  • package/after/パッケージ名☆:そのパッケージが読まれた直後。
    • \AtEndOfPackageFile{<パッケージ名>}{<コード>}〈filehook-ltx〉
  • class/before:何かのクラスが読まれる直前。
    • \AtBeginOfClasses{<コード>}〈filehook-ltx〉
  • class/before/クラス名:そのクラスが読まれる直前。
    • \AtBeginOfClassFile{<クラス名>}{<コード>}〈filehook-ltx〉
  • class/after☆:何かのクラスが読まれた直後。
    • \AtEndOfClasses{<コード>}〈filehook-ltx〉
  • class/after/クラス名☆:そのクラスが読まれた直後。
    • \AtEndOfClassFile{<クラス名>}{<コード>}〈filehook-ltx〉
  • include/before\includeでのファイル読込において、事前の\clearpageの直後。
    • \AtBeginOfIncludes{<コード>}〈filehook-ltx〉
  • include/before/ファイル名:前項と同様で特定ファイルに限ったもの。
    • \AtBeginOfIncludeFile{<ファイル名>}{<コード>}〈filehook-ltx〉
  • include/end☆:\includeでのファイル読込において、事後の\clearpageの直前。
    • \AtEndOfIncludes{<コード>}〈filehook-ltx〉
  • include/end/ファイル名☆:前項と同様で特定ファイルに限ったもの。
    • \AtEndOfIncludeFile{<ファイル名>}{<コード>}〈filehook-ltx〉
  • include/after☆:\includeでのファイル読込において、事後の\clearpageの直後で、書込対象の.auxを本体に戻す前。
    • \AfterIncludes{<コード>}〈filehook-ltx〉
  • include/after/ファイル名☆:前項と同様で特定ファイルに限ったもの。
    • \AfterIncludeFile{<ファイル名>}{<コード>}〈filehook-ltx〉

ファイルを読み込んだときに実行されるコードの順番。

※ 〇印はパッケージまたはクラスの場合のみ。

  • package/beforeまたはclass/beforeの内容。
  • package/before/名前またはclass/before/名前の内容。
  • file/beforeの内容。
  • file/before/名前の内容。
  • ファイル自体の内容。
  • file/after/名前の内容。
  • file/afterの内容。
  • \AtEndOfPackageまたは\AtEndOfClassの内容。
  • package/after/名前またはclass/after/名前の内容。
  • package/afterまたはclass/afterの内容。

まとめ