マクロツイーター

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

例によって去年のアレ(アレ)を振り返ってみる

f:id:zrbabbler:20201122184755g:plain
TeX & LaTeX Advent Calendar 2020

昨年(2019年)の「TeX & LaTeX Advent Calendar」はこんな感じでした。

12/01emojifreakLuaTeXでカラーフォント・絵文字✌(SVG/COLR/CBDT)
12/02t-kemmochixfakebold.styの紹介
12/03p_typoPre-Announcement
12/04ToshioCPLinux, LaTeX, Beamer and Tikz
12/05CareleSmith9[TeX] fontspec について。
12/06uwabamiTeX on Debian 10 (Buster)
12/07やまいjlreqでの箇条書きのインデント調整に関して。
12/08trueroadLuaTeXで原ノ味フォントを使う
12/09munepiぼくのかんがえたさいきょうのLaTeX索引スタイルファイル
12/10mod_poppoTeXConf 2019向けのClutTeX発表資料 | 雑記帳
12/11doraTeXTikZ でベン図をたくさん描いてみよう!
12/12TANNOGitLab を利用して LaTeX 文書を作成する話
12/13mattskalapgfornamentとTsukurimashouでディングバットを書いて
12/14hak7a3TeX言語でレイトレーシングを実装する話
12/15aminophenpTeX のペナルティと LuaTeX-ja のペナルティ
12/16keisuke495500日本列島をLaTeXで表示させたかったのでSVGをTikZに変換するプログラムを作った
12/17t-kemmochiLaTeXの数式を映えさせる
12/18XsLaTeXで目次のカスタマイズをしてみた
12/19hid_alma1026技術書典7で頒布した同人誌の公開
12/20えぬぬんどうしてLaTeXを使うのか、もう一度考えてみる
12/21ToshioCP書籍作成の支援ツール
12/22puripuri2100TeXConf 2019の供養
12/23wtsnjpLuaTeX の普及状況を LuaTeX で調べてみた
12/24golden_luckyTeXの脚注をなんとかする
12/25zr_tex8rマクロとプリミティブの間の壁を打破せよ―LuaTeXで“1回展開”のマクロを定義する話

というわけで、今年はこれを微塵も参考にせずに、自分が素敵だと思うネタを全力でぶつけていきましょう!

今年も例によってTeXでAdvent Calendarする件について

例によって!!

f:id:zrbabbler:20201122184755g:plain
TeX & LaTeX Advent Calendar 2020

TeXLaTeX Advent Calendar 2020

とっておきのTeXLaTeXネタを皆で持ち寄って楽しむ
TeXLaTeX Advent Calendar」
今年で9回目の開催となります。
皆さんの、心をこめた素敵なネタを例によってお待ちしております!
ハッシュタグは「#texadvent2020
TeXLaTeX 初心者大歓迎。 (重要)
TeXLaTeX 非初心者大歓迎。

例によって……コレです!

今年の重点テーマ

今年の重点テーマはコレです。
「このパッケージもスゴイ!」

重点テーマを設定しはじめたのは2014年で、そのときのテーマが 「このパッケージがスゴイ!」でした。 それから6年の時が経ち、新しいパッケージが続々と生まれています。 皆さんの推しパッケージの魅力を存分に語りましょう! 。

スゴイパッケージを知ってるぞ、という人は是非ともそのスゴさを語りましょう。スゴイパッケージが思いつかないという人は、自分でスゴイパッケージをつくってそれを紹介しましょう!(えっ)

例によって「重点テーマ」は「制限」ではありません。

(前略)……以下の何れかテーマに該当する何かを書きます。

「このパッケージもスゴイ!」に大いに関連するTeXLaTeXネタ。
「このパッケージもスゴイ!」にチョット関連するTeXLaTeXネタ。
「このパッケージもスゴイ!」にサッパリ関連しないTeXLaTeXネタ。

このように、例によって以前と同じく、TeXに関連するもの(LaTeX、plain TeX、ConTeXt、llmk、learnlatex.org、Overleaf、Cloud LaTeX、……)なら何でも構いません。

例によって、寒い冬を乗り越えましょう☃︎

「1TeXのロゴ」をSATySFiしたい話

今日は、皆さんお待ちかねの、きりたんぽの日

というわけで早速、SATySFiで「1TeXのロゴ」を出力してみましょう!

texlogoパッケージを使って解決しない話

「SATySFiでTeX関係のロゴ」というと一昨年のクリスマスのネタにありましたね。

というわけで早速……アレ?

f:id:zrbabbler:20201110142612p:plain
texlogoで出力できるロゴの一覧表(test.pdfより)

\OneTeX がない……。(ざんねん🙃)

結局自力で解決する話

仕方がないので、作ってみた。

@require: stdja

let-inline ctx \OneTeX =
  let size = get-font-size ctx in
  let krn r = inline-skip (0pt -' size *' r) in
  let f = read-inline ctx in
  let fE = ctx |> set-manual-rising (0pt -' (size *' 0.15))
               |> read-inline in
  let dsone = math-char MathOrd (string-unexplode [0x1D7D9]) in
  let one = embed-math (set-font-size (size *' 1.44) ctx) dsone in
    one ++ krn 0.2 ++ f {T} ++ krn 0.125 ++ fE {E} ++ krn 0.08 ++ f {X}

in

document (|
  title = {今日はきりたんぽの日!};
  author = {某ZR(アレ)};
  show-title = false; show-toc = false;
|) '<
  +p {I love \OneTeX;!}
>

f:id:zrbabbler:20201110142907p:plain
出力結果

1TeX!1

まとめ

こんなことをやってる場合じゃない(自明)


  1. LaTeXで出力したときのロゴと比べると「1」のフォントが違うが、黒板太字の一種であるのでこれで正当である。

Windows10の游フォントで\CIDしたい話

この辺りの話。

ヤヤコシイ話は抜きにしてとにかく方法を説明する。

①フツーのやり方

kanji-config-updmapでyu-win10をした上で、普通にjapanese-otfパッケージを使う。

\documentclass[uplatex,dvipdfmx,a4paper]{jsarticle}
\usepackage{otf}
\begin{document}
\CID{1722}宴とか\CID{7968}宴とか\CID{20249}宴とか\ajSnowman
\ajMaruAlph{1}\ajKuroMaruAlph{1}\ajKakuAlph{1}\ajKuroKakuAlph{1}%
\ajMaru{12}\ajMaru{34}\ajMaru{56}%CID+10268
\end{document}

あるいは、pxchfonでyu-win10を指定する。

\documentclass[uplatex,dvipdfmx,a4paper]{jsarticle}
\usepackage{otf}
\usepackage[yu-win10]{pxchfon}% 游フォント
\begin{document}% サンプルは前と同じ
\CID{1722}宴とか\CID{7968}宴とか\CID{20249}宴とか\ajSnowman
\ajMaruAlph{1}\ajKuroMaruAlph{1}\ajKakuAlph{1}\ajKuroKakuAlph{1}%
\ajMaru{12}\ajMaru{34}\ajMaru{56}
\end{document}

この場合、システムのToUnicode CMapを利用して「AJ1→Unicode」の変換を行った上でUnicodeでグリフが指定される。だから〈饗〉の異体字は区別されないし、「丸56」(CID+10268)のような「そもそもUnicodeにない字」は出力できない。それから、ToUnicode CMapに漏れがあるため、〈㉞〉(U+325E)や〈🅐〉(U+1F150)のように「実際にはUnicodeになるのにサポートされない」ような文字もある。

②pxchfonでunicodeしてみる

pxchfonパッケージでunicodeオプションを追加する。

\documentclass[uplatex,dvipdfmx,a4paper]{jsarticle}
\usepackage{otf}
\usepackage[unicode,yu-win10]{pxchfon}% unicodeしてみる
\begin{document}% サンプルは前と同じ
\CID{1722}宴とか\CID{7968}宴とか\CID{20249}宴とか\ajSnowman
\ajMaruAlph{1}\ajKuroMaruAlph{1}\ajKakuAlph{1}\ajKuroKakuAlph{1}%
\ajMaru{12}\ajMaru{34}\ajMaru{56}
\end{document}

この場合、Unicodeでグリフ指定する点は同じであるが、「AJ1→Unicode」の変換をもう少し頑張る1ので多少マシになる。さらに、JIS90とJIS2004のフォント(TFM)を両方利用するので、漢字の異体字が「JIS90字形とJIS2004字形に限っては」正しく出力される。上の例だと〈饗〉の異体字について、JIS90字形の\CID{1722}JIS2004字形の\CID{7968}は想定の字形となるが、\CID{20249}はならない。

③裏技「GID指定」

実は、游フォントについては、「AJ1のCIDを2だけずらしたのがGID」という規則になっている2。pxchfonでは「GID指定グリフ出力」の機能があるので、これを組み合わせると、(ほぼ)全てが解決する。

pxchfonでglyphidを指定すると、GID指定グリフ出力のための\gidという命令が使えるようになる。例えば、「丸56」(CID+10268)を出力したい場合は、2だけずらして\gid{10270}とすればよい。

glyphidはエンジンがupLaTeXでかつunicodeが指定されている場合にのみ利用できる。また、グリフが全角幅であることを仮定するのでそうでない場合は出力が異常になることに注意。

\documentclass[uplatex,dvipdfmx,a4paper]{jsarticle}
\usepackage{otf}
\usepackage[unicode,yu-win10,glyphid]{pxchfon}% glyphidしてみる
\begin{document}% うまくいかない文字を \gid に書き直す
\CID{1722}宴とか\CID{7968}宴とか\gid{20251}宴とか\ajSnowman
\ajMaruAlph{1}\ajKuroMaruAlph{1}\ajKakuAlph{1}\gid{11176}%
\ajMaru{12}\ajMaru{34}\gid{10270}
\end{document}

f:id:zrbabbler:20201010235624p:plain

まとめ

游フォントの無表情な☃もなかなかに素敵。


  1. bxjatoucsパッケージで規定する「AJ1→Unicode」変換と同等になる。

  2. ただし、AJ1-7のグリフ(CID+23058~23059)は後から追加された関係で、GIDは337ずれた23395~23396になっている。23060~23394および23397のGIDには「AJ1にないグリフ」が割り当てられている。

メモ:新しい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の内容。

まとめ

llmk自体がアレ

「llmk自体がアレ」記事がなくて夜も眠れない人がいるかもしれないので、作ってみた。

フツーにllmkしたらフツー

フツーのupLaTeX文書を用意する。

[main.tex]
%#!uplatex
\documentclass[uplatex,a4paper]{jsarticle}
\begin{document}
llmkは素敵!
\end{document}

これをllmkでビルドする。

llmk main.tex

uplatexとdvipdfmxが走って、main.pdfが生成される。

$ ls
main.aux  main.dvi  main.log  main.pdf  main.synctex.gz  main.tex

f:id:zrbabbler:20200924045146p:plain

フツー。

--silent付きでllmkしたらアレ

llmkには--silentというオプション(短縮名-s)があって、これを指定すると、中で起動するコマンドの端末出力が抑止される。

先ほどのソースファイルを、今度は--silent付きでビルドする。

※いったん生成ファイルをclobber(llmk -C main.tex)した後で。

llmk -s main.tex

すると今度はDVIは生成されるがPDFが生成されていない。どうやらdvipdfmxが実行していないらしい。

$ ls
main.aux  main.dvi  main.log  main.synctex.gz  main.tex

アレ!

まとめ

というわけで、夜もあんしん🙃


補足

さっそくllmkが修正されたようです。

これでllmk者もあんしん🙂

LaTeX Workshopでllmkしてみたらアレだった件

VSCode上でLaTeXの統合環境を実現するための拡張機能としては「LaTeX Workshop」が事実上の標準となっている。LaTeX Workshopでは「実行するコマンドのリスト」の指定を完全にユーザに委ねる設計になっている。このため、原理的には、「llmk」などの新しいビルドツールと組み合わせて使えることが期待できる。

一週間ほど前に、めでたくllmkがCTANTeX Liveに収録されたので、さっそく「LaTeX Workshopとllmkを組み合わせる」設定を試してみたら、LaTeX Workshopがアレなせいでアレだった、という話。

LaTeX Workshopやllmk自体に関する解説は一切省略する。

なぜLaTeX Workshopでllmkしたかったか

基本的にLaTeX Workshopのビルドは「ある決まったコマンドの列(レシピと呼ばれる)を実行する」というものなので、異なる「コマンドの列」を必要とする文書を扱うたびにレシピを(メニューから)切り替える必要がある。LaTeX WorkshopではLatexmkを呼び出すレシピが既定で登録されていて、Latexmkを使うとある程度は「コマンドの列」の違いを吸収できるが、Latexmkには「利用するTeXエンジンをソースファイルから判別する」機能は持っていないため、エンジン選択の煩雑さは残る。

一方、llmkを使うと「利用するTeXエンジンをソースファイルから判別する」ことが可能になるため、「レシピの切替」の考慮が全く不要になって、幸せになると考えた次第である。

LaTeX Workshopを設定した

llmk <ファイル名>」だけを実行するレシピを登録する。

[setting.json 追記]
{
    //...
    "latex-workshop.latex.recipes": [
        {
            "name": "llmk",
            "tools": [
                "llmk"
            ]
        },
        // 他のレシピ設定...
    ],
    "latex-workshop.latex.tools": [
        {
            "name": "llmk",
            "command": "llmk",
            "args": [
                "%DOC%"
            ],
            "env": {}
        },
        // 他のツール設定...
    ],
    // ...
}

簡単な文書でテストした

「uplatex→dvipdfmx」でビルドできる単純なupLaTeX文書を用意した。

※先頭行にllmkのためのマジックコメントがある。

[main.tex]
%#!uplatex
\documentclass[uplatex,dvipdfmx,9pt,a5paper]{jsarticle}
\usepackage[scale=0.9]{geometry}
\usepackage{scsnowman}
\begin{document}
\section{\TeX}
{\TeX}はアレ。
\section{結論☃}
{\centering\scsnowman[scale=10, 
  hat,snow,arms,buttons,muffler=red]\par}
\end{document}

これをコマンドシェルからllmkでビルドしてみる。

llmk main.tex

すると、「uplatex2回1→dvipdfmx」が実行されて正常にビルドが完了する。

f:id:zrbabbler:20200923164449p:plain
PDF出力(素敵)

では、このソースファイルをVSCodeで開いて、LaTeX Workshopの「llmk」レシピでビルドしてみる。

f:id:zrbabbler:20200923165317p:plain
LaTeX WorkshopでビルドしてPDFを表示した様子

これは想定通りの結果になった。

チョット複雑な文書でテストした

フツーにllmkしたら想定通り

続いて、相互参照やbibファイルからの文献参照を含んだ、チョット複雑なupLaTeX文書を用意してみた。

※このソースには一か所ミスがある。8行目に\ref{sec:sconclusion}とあるが、これは\ref{sec:conclusion}の誤りである。

[main.tex]
%#!uplatex
\documentclass[uplatex,dvipdfmx,9pt,a5paper]{jsarticle}
\usepackage[scale=0.9]{geometry}
\usepackage{scsnowman}
\begin{document}
\section{はじめに}
\ref{sec:tex}節で{\TeX}\cite{texbook}についてdisる。
\ref{sec:sconclusion}節で結論を述べる。
\section{\TeX}\label{sec:tex}
{\TeX}はアレ。
\section{結論☃}\label{sec:conclusion}
{\centering\scsnowman[scale=10, 
  hat,snow,arms,buttons,muffler=red]\par}
\bibliographystyle{plain}\bibliography{main}
\end{document}
[main.bib]
@Book{texbook,
  author =       "Donald Ervin Knuth",
  title =        "The {\TeX}book",
  volume =       "A",
  publisher =    "Addison-Wesley",
  address =      "Reading, MA, USA",
  edition =      "23rd printing, with corrections.",
  pages =        "ix + 483",
  year =         "1993",
  series =       "Computers and typesetting",
}

先ほどと同様にコマンドシェルで「llmk main.tex」でビルドすると、「uplatex2回→bibtex→uplatex2回→dvipdfmx」と実行されて次の出力が得られる。

\refの誤りのところが??になっている。

f:id:zrbabbler:20200923171909p:plain
PDF出力(素敵)

この文書では\refの引数が一か所誤っているため、解決できない参照が最後まで残り、最後のuplatexの実行において以下の警告が発生している。

LaTeX Warning: Reference `sec:sconclusion' on page 1 undefined on input line 8.

LaTeX Warning: There were undefined references.

LaTeX Workshopでllmkしたらアレ

では、このソースファイルをLaTeX Workshopでビルドしたらどうなるか。

f:id:zrbabbler:20200923174050p:plain
LaTeX WorkshopでビルドしてPDFを表示した様子

出力されたPDFを見ると、先ほど示したものと全く同じになっている。つまり、llmkの実行は最後のuplatexの実行まで想定通りに行われたと推定できる。

しかし、今回のビルドの結果には大きな問題がある。それは「PROBLEMS」の表示である。

f:id:zrbabbler:20200923183739p:plain
アレな警告の通知

ここにはLaTeXの実行中に発生したエラーや警告の内容が掲示される。先述の通り、このソースファイルをビルドすると、最終的に2個の警告が発生するはずである。ところが実際にはPROBLEMSには14個もの警告が出ている。しかも、よく見ると、最終的には正常に解決しているはずのsec:texや文献texbookに対しても「参照未解決」の警告が出ているのである。

sec:textexbookの参照は初回のuplatexの実行のときは解決できないはずである。参照未解決の警告の異常な多さを考えると、「途中のものを含めて全てのuplatexの実行における警告をLaTeX Workshopが拾ってしまっている」と推定できる。

※途中の警告を拾うため、仮に「誤りがなくて最終的に全ての参照が解決されるLaTeX文書」をビルドした場合でも警告が発生したような表示になる。

やっぱり、LaTeX Workshopがアレ

本来問題がないはずの箇所でエラー・警告が通知されるのではPROBLEMS機能がまるで役に立たない。「最後のLaTeXエンジンの実行」で発生したエラー・警告だけを拾うべきなのは明らかだろう。

しかし冷静に考えてみると、「最後のLaTeXエンジンの実行」だけ見る、というのは無理な要求であると思えてくる。なぜなら、LaTeX Workshopからみると、ビルドにおいては「llmk」という単一のコマンドを実行したにすぎないわけで、その標準出力や標準エラーのテキストから「LaTeXのエラー・警告」を拾うとしても、「最後のLaTeXエンジンの実行」に相当する部分を抜粋できそうにない……。

……いや待て、全く同じことはLatexmkを使う場合にも成立するはずである。しかしLaTeX Workshopが既定で設定を登録しているくらいだから、Latexmkで同じ問題が起こっているはずがない。実際に、Latexmkで(upLaTeX用の設定を用意して)ビルドしてみると、PROBLEMSの通知は正常(警告2個)になる。なぜLatexmkの場合は「最後のLaTeXエンジンの実行」の部分を抜粋できるのだろうか。

チョットLaTeX Workshopのソースコードを眺めてみると……あっ!

f:id:zrbabbler:20200923185614p:plain

なんと、Latexmk専用の処理が書かれている!

Latexmkが実際にコマンドを起動する際に次のようなメッセージを端末に出力しているのだが:

Latexmk: applying rule 'latex'

要するに、LaTeX Workshopは、このメッセージを目印にして「抜粋」の処理を行っているようである。

試しに、llmkのプログラムに細工をして、このLatexmkのメッセージを出力するようにしてみると……。

f:id:zrbabbler:20200923183647p:plain
マトモな警告の通知

ああっ、PROBLEMSの通知が正常になった!

………………これはひどい

まとめ


  1. \sectionの情報が.auxファイルに書き出されるため、uplatexは2回実行される。