マクロツイーター

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

「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回実行される。

LaTeXで和文を斜体する話(1)

まとめてみた。

なぜ和文を斜体しにくいのか

ほとんどの場合、日本語用のフォントには「斜体」の変種は用意されていない1。従って、和文を斜体で出力しようとすると「合成斜体」(直立体のグリフを変形して出力する)を使うことになる。TeXを利用したワークフローにおいても合成斜体は十分にサポートされているのであるが、あまり好まれていないこともあり、LaTeXユーザが簡単に使えるようにはなっていない。特に、「通常のフォント切り替え命令(\textit\slshape等)で和文に斜体が適用される」ような設定はほとんど行われないようである。

“図”として斜体する

斜体の使われ方に関する欧文と和文の間の大きな違いとして「和文の斜体は本文の段落を組むのに使われることが原則的にない」ということがある。つまり和文の斜体の用法の大部分は「図に類するもの(装飾性の高いもの)」ということになるだろう。そういう用法の場合、「フォントとしての斜体」ではなく「図の装飾としての斜体変形」を利用するのが妥当である。

TikZで図を作成する場合、テキストを配置するためにノード(node命令)を使うことになる。ここでnode命令のオプションとしてxslant=<割合>を指定すると、そのノードに斜体変形を適用できる。

例えば次のTikZの文は現在点に25%の斜体変形をかけた「テキスト」の文字を出力する。

\node[xslant=0.25] {テキスト};

※もちろん、ノードのテキストの内容は和文文字に限らない。

「斜体変形をかけた文字を含む図」の実例を挙げておく。

% upLaTeX+dvipdfmx
\documentclass[dvipdfmx]{article}
%↓単体の図を出力するための準備
\usepackage[papersize={400bp,320bp},
  margin=0pt,noheadfoot]{geometry}
\usepackage[svgnames]{xcolor}
\usepackage{tikz,scsnowman,tikzducks}
\setlength{\parindent}{0pt}
%↓既定のフォントを"原ノ味ゴシックHeavy"にする
\usepackage{lmodern,pxchfon}
\setminchofont{HaranoAjiGothic-Heavy.otf}
\newcommand{\myPeace}{\fontsize{64}{0}\rmfamily}
\begin{document}
\begin{tikzpicture}[x=1bp,y=1bp]
%↓この辺はいつものやつ☃
\useasboundingbox (0,0) rectangle (400,320);
\node at (100,100) {\scsnowman[scale=20,
  muffler=Red,hat=Green,buttons=RoyalBlue,
  arms=Brown,snow=SkyBlue]};
\node at (300,92) {\tikz[scale=60]{\duck[
  hat=Brown,cape=Orange]}};
%
%↓斜体変形をかけた文字を出力する
\node[text=Green,font=\myPeace,xslant=0.25]
  at (200,250) {世 界 平 和};
\end{tikzpicture}
\end{document}

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

TikZの図は段落の中に配置できる(“インライン”の要素である)ので、「図としての斜体」を“流用”する形で文章の一部を斜体の文字で出力することも可能である。ただし元来が図のための機能であるため、そういう使い方には少しコツが要る。詳しい説明は別の記事に譲ることにして、ここでは「斜体変形をかけたテキスト」を出力するマクロを挙げておく。

%% \mySlanted{テキスト}: テキストを25%斜体変形付で出力する.
\newcommand*{\mySlanted}[1]{\tikz[baseline=(T.base)]{%
  \node[inner sep=0pt,outer sep=0pt,xslant=0.25](T){#1}}}

この\mySlantedを使ったupLaTeX文書の例を挙げておく。

% upLaTeX+dvipdfmx
\documentclass[uplatex,dvipdfmx,a4paper]{jsarticle}
%↓ゴシック体を"原ノ味ゴシックBold"にする
\usepackage[haranoaji]{pxchfon}
\setgothicfont{HaranoAjiGothic-Bold.otf}
\usepackage{tikz}
\newcommand*{\mySlanted}[1]{\tikz[baseline=(T.base)]{%
  \node[inner sep=0pt,outer sep=0pt,xslant=0.25](T){#1}}}
\begin{document}
毎年9月21日は\mySlanted{\gtfamily 世界平和}の日☃です。
\end{document}

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

ただし、この方法には重大な欠点がある。単に「図を文章内に出力している」に過ぎないので、テキストを途中で改行することができないのである。従って、例えば「段落全体を斜体文字で出力したい」という場合2にはこの方法は使えず、きちんと「フォントとしての斜体」を用意する必要がある。

LuaLaTeXでフォントとして斜体する

LuaLaTeX(やXeLaTeX)ではfontspecの機能が使えるため、合成斜体のフォントを利用するのはそれほど難しくない。

fontspecの機能(\setmainfont\fontspec等)でフォントを定義する際に、パラメタにFakeSlant=割合を指定すると合成斜体が適用される。例えば、以下のコードは欧文フォントを25%斜体の「Comic Sans MS」に変更する。

% 欧文を25%斜体の"Comic Sans MS"にする
\fontspec{Comic Sans MS}[FakeSlant=0.25]

LuaTeX-jaで和文を扱っている場合は、luatexja-fontspecの命令(\setmainjfont\jfontspec等)を使って同様にFakeSlantを指定すれば合成斜体の和文フォントが利用できる。

% 和文を25%斜体の"HGS創英角ポップ体"にする
\jfontspec{HGSSoeiKakupoptai}[FakeSlant=0.25]

\newjfontfamilyを使って斜体の和文フォントを定義する例を挙げる。

% LuaLaTeX分署; UTF-8
\documentclass[a6paper]{ltjsarticle}
\usepackage{luatexja-fontspec}
% 斜体の"HGS創英角ポップ体"の和文フォント定義
\newjfontfamily{\myPoptai}{HGSSoeiKakupoptai}[FakeSlant=0.25]
\begin{document}
\myPoptai
日本國民は、正義と秩序を基調とする國際平和を誠實に希求し、
國權の發動たる戰爭と、武力による威嚇又は武力の行使は、
國際紛爭を解決する手段としては、永久にこれを放棄する。
\end{document}

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

「フォントをイタリック(\itshape)に変更した場合に和文に合成斜体が適用されるようにしたい」という場合は、ItalicFeaturesというパラメタを使って次のように指定できる。

% ItalicFeaturesの中に書いたパラメタはitshapeの時だけ
% 適用される
\newjfontfamily{\myPoptai}{HGSSoeiKakupoptai}
  [ItalicFeatures={FakeSlant=0.25}]

あるいは、この指定と等価になるAutoFakeSlantというパラメタも用意されている。

% itshapeを合成斜体にする
\newjfontfamily{\myPoptai}{HGSSoeiKakupoptai}[AutoFakeSlant=0.25]

AutoFakeSlantを指定して和文で合成斜体を有効にする例を挙げておく。

% LuaLaTeX分署; UTF-8
\documentclass[a6paper]{ltjsarticle}
\usepackage{luatexja-fontspec}
%↓itshapeを合成斜体にする
\setmainjfont{Harano Aji Mincho}[AutoFakeSlant=0.25]
\setsansjfont{Harano Aji Gothic}[AutoFakeSlant=0.25]
\usepackage{bxjalipsum}
\begin{document}

山路を登りながら、こう考えた。

智に働けば、\textit{ゆきだるま☃。}
情に棹させば、\textit{ゆきだるま☃。}
意地を通せば、\textit{ゆきだるま☃。}
\sffamily
とかくに人の世は、\textit{ゆきだるま☃。}

\end{document}

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

(もしかして続くかも)


  1. デザイン性の高いフォントの中には、元々斜体としてデザインされているものもある。

  2. アッ、さっき言った原則に反しているゾ!

tcspingifで“もっともっと”ゆきだるまを動かす

今日は「ホウキ付ゆきだるま☃」の日!

f:id:zrbabbler:20200818191245g:plain

先日の「ゆきだるま☃の日」にはLaTeXで「回転ゆきだるま☃画像」を作成するためのtcspingifというスクリプトを紹介した。tcspingifを使って作れる「回転☃」は「自転☃」であるが、少し(あるいは、かなり)頑張れば、上図のような「公転☃」、あるいはもっと別の動きを伴った「動く☃」のGIF画像をつくることもできる。その方法について解説する。

tcspingifはなぜ動くのか

tcfaspinパッケージはanimateパッケージの機能を使って「動くPDF文書」を作っているが、「動くPDF文書」は画像に変換できない。では、tcspingifはなぜGIF画像を作れるのかというと、実はtcfaspinの中に秘密がある。

tcfaspinを読み込む前に次のようなオマジナイを実行しておく。

\chardef\faStopTicks=18 \chardef\faAllTicks=32

TeX言語のコードなので理解する必要は全くないのだが、フインキとしては「\faStopTicks\faAllTicksという2つの“変数”を定義して、それぞれ18と32を“代入”する」のようなことを行っている1。このオマジナイが実行済である場合、tcfaspinパッケージはanimateを読み込まなくなり、また、\faSpin命令の動作が変わり、単に「32フレーム中の18フレーム目」の状態を出力するようになる。この場合に出力されるPDF文書はanimateを使っていないため普通にImageMagickで画像に変換できる。

この「tcfaspinの裏仕様」を利用して、tcfaspinは次のような手順で「動くGIF画像」を作っている。(フレーム数が32だとする。)

  • n=0~31の各々について:
    • 上記のオマジナイ(ただし\faStopTicksをnとする)を実行した上で対象のLaTeXソースをコンパイルして、普通のPDF文書を作る。
    • ImageMagickでPDF文書をPNG画像に変換する。これが「nフレーム目のPNG画像」となる。
  • 再びImageMagickを用いて、「0~31フレーム目のPNG画像」をアニメGIF画像に変換する。

tcfaspinしないでtcspingifする

このtcfaspinの実行手順をよく見ると、それ自体はtcfaspinとは無関係であることがわかる。すなわち、対象のLaTeX文書は別にtcfaspinを読み込んでいる必要はなく、単に「オマジナイが実行されて、“変数”が定義されている」という点だけが“普通のコンパイル”と異なるわけである。

これを利用すると、tcspingifを“流用”して好きな動き方をするアニメGIF画像をつくることができる。すなわち、LaTeX文書の内容を、「\faAllTicksフレーム中の\faStopTicksフレーム目」の状態を出力するようにすればよいのである。

pgfmathパッケージ2の機能を利用すると、“変数”の値を読み出すことができる3

% "変数" \faStopTicks の値を \vN に代入する
\pgfmathsetmacro{\vN}{\number\faStopTicks}
% \faStopTicks ÷ \faAllTicks の値を \vT に代入する
\pgfmathsetmacro{\vT}{\number\faStopTicks/\number\faAllTicks}

要するに要するに

以下のような感じでコードを書けばよい。

\documentclass{standalone}
\usepackage{tikz,pgfmath}
% \vT は"アニメの開始を0、終了を1とした場合の現在時刻"の値
\pgfmathsetmacro{\vT}{\number\faStopTicks/\number\faAllTicks}
% \vN は"現在何フレーム目か"の値
\pgfmathsetmacro{\vN}{\number\faStopTicks}
% (その他諸々の設定...)
\begin{document}
\begin{tikzpicture}
% \vT や \vN の値を利用して"現在のフレーム"を描画する
\end{tikzpicture}
\end{document}

※もちろん、このようにtcspingifを“流用”したLaTeX文書は「tcspingif専用」であり、普通にコンパイルして「動くPDF文書」をつくることはできないことに注意。
※tcfaspinを読み込まない場合、tcfaspinの設定からフレーム数を取得することができないため、tcspingifで--ticks(-t)オプションの指定が必須になる。

ゆきだるま☃を動かしてみる

※以下では、前回と同じく「DPI値を72にした(tcspingifで-d 72を指定)上でTikZでx=1bp,y=1bpを指定して座標の単位をピクセルと一致させる」設定を利用する。

手始めに、ゆきだるま☃を単純に横に動かしてみる。具体的に「600×120ピクセルの画像で、ゆきだるま☃を(60,60)から(540,60)に動かす」ことにする。先ほど示した雛形の\vT(現在時刻)を使うことにすると、ゆきだるま☃を置く点は「始点・終点間の\vT倍内分点」となる。calcライブラリの内分点表記を利用すると、tikzpicture内は以下のように書ける。

\node at ($(60,60)!\vT!(540,60)$) {
  \scsnowman[scale=15,hat,snow,arms,buttons,muffler=red]};

全体のソースは以下のようになる。

[sample01.tex]
% ↓以下のコマンドで変換
% tcspingif -e pdflatex -d 72 -t 32 -b 1 sample01.tex
\documentclass{standalone}
\usepackage{xcolor,tikz,pgfmath}
\usepackage{scsnowman}% ゆきだるま☃!
\usetikzlibrary{calc}% 内分点表記を使う
% \vT は"現在時刻"(0~1の範囲)
\pgfmathsetmacro{\vT}{\number\faStopTicks/\number\faAllTicks}
\begin{document}
\begin{tikzpicture}[x=1bp,y=1bp]
\useasboundingbox (0,0) rectangle (600,120);
\node at ($(60,60)!\vT!(540,60)$) {
  \scsnowman[scale=15,hat,snow,arms,buttons,muffler=red]};
\end{tikzpicture}
\end{document}

このLaTeXソースを以下のコマンドで画像に変換する。

tcspingif -e pdflatex -d 72 -t 32 -b 1 sample01.tex

※以下、ソースリストの先頭のコメントに変換用のコマンドラインを載せる。

実際に画像を表示して結果を確認してみよう。

f:id:zrbabbler:20200818191540g:plain

これで「自分で動きを定義する」ことができた。あとは“\vTと描画の間の処理”を適宜増やしていくことで、好きな動きを表現するだけである。

まずは「ゆきだるま☃が始点と終点の間を往復する」ようにしてみよう。pgfmathの式の三項演算子を利用して、「\vTが0→1を動くと、\vAが0→1→0と動く」ような新たな変数\vAを定義する。

\cLet{\vA}{(\vT<0.5) ? 2*\vT : 2-2*\vT}

あとは、\nodeの座標において、\vTの代わりに\vAを使う。

\node at ($(60,60)!\vA!(540,60)$) {

全体のソースは以下の通り。ここで、\pgfmathsetmacroという命令名は長いので、\cLetというマクロで別名定義している。

[sample02.tex]
% tcspingif -e pdflatex -d 72 -t 32 -b 1 sample02.tex
\documentclass{standalone}
\usepackage{xcolor,tikz,pgfmath,scsnowman}
\newcommand{\cLet}{\pgfmathsetmacro}
\usetikzlibrary{calc}
\cLet{\vT}{\number\faStopTicks/\number\faAllTicks}
\cLet{\vA}{(\vT<0.5) ? 2*\vT : 2-2*\vT}
\begin{document}
\begin{tikzpicture}[x=1bp,y=1bp]
\useasboundingbox (0,0) rectangle (600,120);
\node at ($(60,60)!\vA!(540,60)$) {
  \scsnowman[scale=15,hat,snow,arms,buttons,muffler=red]};
\end{tikzpicture}
\end{document}

f:id:zrbabbler:20200818191556g:plain

今度は「ゆきだるま☃の動きをもう少し自然にする」ために、水平位置の変数\vA\vTの二次関数に変えてみる。

\cLet{\vB}{(\vT<0.5) ? 2*\vT : 2-2*\vT}
\cLet{\vA}{(\vA<0.5) ? 2*\vB*\vB : -2*\vB*\vB+4*\vB-1}
[sample03.tex]
% tcspingif -e pdflatex -d 72 -t 32 -b 1 sample03.tex
\documentclass{standalone}
\usepackage{xcolor,tikz,pgfmath,scsnowman}
\newcommand{\cLet}{\pgfmathsetmacro}
\usetikzlibrary{calc}
\cLet{\vT}{\number\faStopTicks/\number\faAllTicks}
\cLet{\vB}{(\vT<0.5) ? 2*\vT : 2-2*\vT}
\cLet{\vA}{(\vA<0.5) ? 2*\vB*\vB : -2*\vB*\vB+4*\vB-1}
\begin{document}
\begin{tikzpicture}[x=1bp,y=1bp]
\useasboundingbox (0,0) rectangle (600,120);
\node at ($(60,60)!\vA!(540,60)$) {
  \scsnowman[scale=15,hat,snow,arms,buttons,muffler=red]};
\end{tikzpicture}
\end{document}

f:id:zrbabbler:20200818191616g:plain

さらに、ゆきだるま☃に回転を加えてみよう。これは\node命令にrotateパラメタを追加するだけである。

[sample04.tex]
% tcspingif -e pdflatex -d 72 -t 32 -b 1 sample04.tex
\documentclass{standalone}
\usepackage{xcolor,tikz,pgfmath,scsnowman}
\newcommand{\cLet}{\pgfmathsetmacro}
\usetikzlibrary{calc}
\cLet{\vT}{\number\faStopTicks/\number\faAllTicks}
\cLet{\vB}{(\vT<0.5) ? 2*\vT : 2-2*\vT}
\cLet{\vA}{(\vA<0.5) ? 2*\vB*\vB : -2*\vB*\vB+4*\vB-1}
\begin{document}
\begin{tikzpicture}[x=1bp,y=1bp]
\useasboundingbox (0,0) rectangle (600,120);
\node[rotate=-360*\vA] at ($(60,60)!\vA!(540,60)$) {
  \scsnowman[scale=15,hat,snow,arms,buttons,muffler=red]};
\end{tikzpicture}
\end{document}

f:id:zrbabbler:20200818191635g:plain

もっと動かしてみる

某ZR氏のツイッタァーでよくみるやつ

「無限に並んだゆきだるま☃が流れている」画像。

  • ゆきだるま☃を8つ横に並べる。
  • その上で、ゆきだるま☃6つ分の大きさの“ビューポート”を右方向に移動させる。これで、ゆきだるま☃が左に流れているように見える。
  • 開始時と終了時の状態を同じにしておくと、アニメガ反復したときに、ゆきだるま☃が滞りなく流れているように見える。
[sample05.tex]
% tcspingif -e pdflatex -d 72 -t 32 -b 1.5 sample05.tex
% ↑少し速度を上げた
\documentclass{standalone}
\usepackage{xcolor,tikz,pgfmath,scsnowman}
\newcommand{\cLet}{\pgfmathsetmacro}
\cLet{\vT}{\number\faStopTicks/\number\faAllTicks}
\begin{document}
\begin{tikzpicture}[x=1bp,y=1bp]
% ビューポート左下座標を (0,0)→(200,0) と動かす.
% "+(...)"は相対指定.
\useasboundingbox (200*\vT,0) rectangle +(600,120);
% 赤マフラー☃と青マフラー☃を交互に4回描く
\foreach \vJ in {0,...,3} {
  \node at (50+200*\vJ,60) {
    \scsnowman[scale=14,hat,snow,arms,buttons,muffler=red]};
  \node at (150+200*\vJ,60) {
    \scsnowman[scale=14,hat,arms,buttons,muffler=blue,
        sweat,mouthshape=tight]};
}
\end{tikzpicture}
\end{document}

f:id:zrbabbler:20200818191651g:plain

増殖ゆきだるま☃

“現在時刻”ではなく“現在何フレーム目か”の値を利用した例。

% \vN は"現在何フレーム目か"の値
\pgfmathsetmacro{\vN}{\number\faStopTicks}
  • フレーム数を5とする。
  • nフレーム目(n=0~4)で「n+1個のゆきだるま☃」を描く。
[sample06.tex]
% tcspingif -e pdflatex -d 72 -t 5 -b 1 sample06.tex
% ↑フレーム数(-t)は5で固定
\documentclass{standalone}
\usepackage[svgnames]{xcolor}
\usepackage{tikz,pgfmath,scsnowman}
\newcommand{\cLet}{\pgfmathsetmacro}
% \vN は"現在何フレーム目か"の値
\cLet{\vN}{\number\faStopTicks}
\begin{document}
\begin{tikzpicture}[x=1bp,y=1bp]
\useasboundingbox (0,0) rectangle (600,120);
\foreach \vJ in {0,...,\vN} {% 上限が \vN
  \node at (60+120*\vJ,60) {
    \scsnowman[scale=15,muffler=Red,hat=Green,
        buttons=RoyalBlue,arms=Brown,snow=SkyBlue]};
}
\end{tikzpicture}
\end{document}

f:id:zrbabbler:20200818191730g:plain

ゆきだるま☃ウェーブ

pgfmathでは初等関数が利用できるので、sin関数を使った例。

[sample07.tex]
% tcspingif -e pdflatex -d 72 -t 32 -b 1.5 sample07.tex
\documentclass{standalone}
\usepackage{xcolor,tikz,pgfmath,scsnowman}
\newcommand{\cLet}{\pgfmathsetmacro}
\cLet{\vT}{\number\faStopTicks/\number\faAllTicks}
\begin{document}
\begin{tikzpicture}[x=1bp,y=1bp]
\useasboundingbox (0,0) rectangle (600,120);
\foreach \vJ in {0,...,9} {
  % xcolorの混合率の値, 100→0 と動く.
  \cLet{\vC}{100*(1-\vJ/9)}
  % 垂直変位. 水平位置と時刻で決まる.
  \cLet{\yD}{30*sin(360*(\vJ/9+\vT))}
  \node at (30+60*\vJ,60+\yD) {
    \scsnowman[scale=9,arms,buttons,
      muffler=red!\vC!blue,hat=red!\vC!blue!75!black]};
}
\end{tikzpicture}
\end{document}

f:id:zrbabbler:20200818191705g:plain

もっともっと動かしてみる

過去にツイッタァーなどで披露した「動く☃画像」のソースを出しておく。

世界平和

[sample08.tex]
% tcspingif -e pdflatex -d 200 -t 32 -b 0.5 sample08.tex
% ↓200dpiで480×480ピクセルになるように調整
\documentclass[margin=1.36bp]{standalone}
\usepackage{xcolor,tikz,pgfmath}
\usepackage{scsnowman,tikzducks}
\definecolor{mc0}{rgb}{0.8,0,0}
\definecolor{mc1}{rgb}{0.5,0.3,0}
\definecolor{mc2}{rgb}{0.2,0.6,0}
\definecolor{mc3}{rgb}{0,0.7,0.1}
\definecolor{mc4}{rgb}{0,0.4,0.4}
\definecolor{mc5}{rgb}{0,0.1,0.7}
\definecolor{mc6}{rgb}{0.2,0,0.6}
\definecolor{mc7}{rgb}{0.5,0,0.3}
\scsnowmandefault{hat,arms,buttons,snow,scale=6}
\newcommand{\cLet}{\pgfmathsetmacro}
\cLet{\vT}{\number\faStopTicks/\number\faAllTicks}
\cLet{\vD}{\vT*360}
\pagecolor{black!0.5}
\begin{document}%↓座標単位は既定のまま(1cm)
\begin{tikzpicture}
\useasboundingbox (-3,-3) rectangle (3,3);
\node at (-  0+\vD:2) {\scsnowman[muffler=mc0]};
\node at (- 45+\vD:2) {\scsnowman[muffler=mc1]};
\node at (- 90+\vD:2) {\scsnowman[muffler=mc2]};
\node at (-135+\vD:2) {\scsnowman[muffler=mc3]};
\node at (-180+\vD:2) {\scsnowman[muffler=mc4]};
\node at (-225+\vD:2) {\scsnowman[muffler=mc5]};
\node at (-270+\vD:2) {\scsnowman[muffler=mc6]};
\node at (-315+\vD:2) {\scsnowman[muffler=mc7]};
\node at (0,0) {\tikz[scale=0.4]{\duck}};
\end{tikzpicture}
\end{document}

f:id:zrbabbler:20200818191843g:plain

フルパワー清涼感

これは普通にtcfaspinを使っている。

[sample09.tex]
% tcspingif -e pdflatex -d 72 -t 32 -b 3 sample09.tex
\documentclass{standalone}
\usepackage{ifthen,xcolor,tikz,pgfmath,scsnowman}
\newcommand{\cLet}{\pgfmathsetmacro}
\pagecolor{blue!30!green!6}%←背景色
\colorlet{coolhat}{blue!80!green!75!black}
\colorlet{coolmuffler}{blue!50!green}
\colorlet{coolarms}{green!30!black!50}
\colorlet{coolsnow}{blue!80!green!40}
\usepackage{tcfaspin}%←回します☃
\newcommand\cESnowman[1]{%
  \cPutSnowman{muffler=coolmuffler,hat=coolhat,#1}}
\newcommand\cOSnowman[1]{%
  \cPutSnowman{muffler=coolhat,hat=coolmuffler,#1}}
\newcommand\cPutSnowman[1]{%
  \node at (\vX*100+50,\vY*100+50) {%
    \faSpin{\scsnowman[%
      scale=10,arms=coolarms,snow=coolsnow,buttons=coolsnow,
      #1]}};}
\begin{document}
\begin{tikzpicture}[x=1bp,y=1bp]
\useasboundingbox (0,0) rectangle (600,600);
\foreach \vX in {0,1,...,5}
  \foreach \vY in {0,1,...,5} {
    % pgfmathの条件演算子だけだと実行の分岐ができないので
    % ifthenを併用する. ただし, \ifthenelse の条件文には
    % 式が書けず不便なので, "pgfmathで真偽値を求めて変数に
    % 入れて \ifthenelse でそれを見る"ことにする.
    \cLet{\vBroom}{\vX==5 && \vY==2}
    \cLet{\vNote}{\vX==4 && \vY==4}
    \cLet{\vOdd}{isodd(\vX+\vY)}
    \ifthenelse{\vBroom>0}{% ホウキ付き
      \cOSnowman{broom=coolarms!75!black}
    }{\ifthenelse{\vNote>0}{% ♪付き
      \cESnowman{note=coolsnow!90}
    }{\ifthenelse{\vOdd>0}{
      \cOSnowman{}
    }{\cESnowman{}}}}
  }
\end{tikzpicture}
\end{document}

f:id:zrbabbler:20200818192013g:plain

ホウキ付きゆきだるま☃の日のやつ

[sample10.tex]
% tcspingif -e pdflatex -d 72 -t 64 -b 0.5 sample10.tex
\documentclass{standalone}
\usepackage{xcolor,tikz,pgfmath,scsnowman}
\usepackage{type1cm}
%↓アホなフォント
\newcommand*{\fAho}{\usefont{OT1}{cmfr}{m}{it}{}}
\newcommand*{\cSize}[1]{\fontsize{#1}{0}\selectfont}
\definecolor{mydbrown}{rgb}{0.3,0.0,0.0}
\definecolor{mylbrown}{rgb}{0.7,0.5,0.0}
\newcommand{\cLet}{\pgfmathsetmacro}%
\cLet{\vT}{\number\faStopTicks/\number\faAllTicks}
\cLet{\vOR}{6}% ☃の軌道の半径
\cLet{\yW}{-2}% ☃のy座標
\cLet{\vD}{2}% キャンバスの位置(z=−2にある)
\cLet{\vA}{\vT*360+2}% 軌道上の角度
%↑開始時に中央に見えるように開始地点を補正している
\cLet{\zW}{\vOR*(1-cos(\vA))}% ☃のz座標
\cLet{\xW}{\vOR*(sin(\vA))}% ☃のx座標
\cLet{\vCS}{\vD/(\vD+\zW)}% (一点透視による倍率)
\cLet{\xC}{\vCS*\xW}% ☃のキャンバス上x座標
\cLet{\yC}{\vCS*\yW}% ☃のキャンバス上y座標
\cLet{\vSS}{\vCS*24}% ☃のキャンバス上の大きさ
%↑実際の大きさ(scale)が24
\newcommand{\cSnowman}[1]{%
  \scsnowman[scale=#1,
    arms=mydbrown,muffler=red,broom=mylbrown]}
\begin{document}
\begin{tikzpicture}[x=1bp,y=1bp]
  \useasboundingbox (0,0) rectangle (256,320);
  \node at (\xC*50+128,\yC*50+286) {\cSnowman{\vSS}};
  %↓例の日めくりカレンダーのアレ
  \node at (128,260) {%
    \cSize{29}\fAho August};
%  \node at (128,168) {%
%    \cSize{190}\fAho 18};
  \node[text=black!50] at (128,68) {%
    \cSize{10}\fAho Broom Snowman Day};
  \node at (128,33) {%
    \cSize{33}\fAho Tuesday};
\end{tikzpicture}
\end{document}

f:id:zrbabbler:20200818191245g:plain

Happy Snowman’s Day☃

[snowmansday2020.tex]
% tcspingif.pl -e pdflatex -t 64 -d 108 snowmansday2020.tex
\documentclass{standalone}
\usepackage[T1]{fontenc}
\usepackage{lmodern,pifont}
\usepackage{xcolor,scsnowman,tikzducks,pgfmath}
\newcommand*{\cLet}{\pgfmathsetmacro}
\setlength{\unitlength}{1bp}
%↓背景色
\colorlet{bgcolor}{blue!30!green!10}
\pagecolor{bgcolor}
%
\cLet{\xCS}{80}\cLet{\yCS}{320}% キャンバスサイズ
\cLet{\vT}{\number\faStopTicks/\number\faAllTicks}
\cLet{\vST}{(\vT-0.5)*2}
\cLet{\yH}{\yCS-\xCS}
\cLet{\xT}{\xCS/2}% ☃のx座標
%↑ただし実際にはずらして(2つ)置いている
\cLet{\yT}{\yH+\xCS/2-\yH*\vST*\vST}% ☃のy座標
%↑時刻の二次関数
\cLet{\vEA}{exp(\vST*5)}\cLet{\vEB}{exp(\vST*(-5))}
\cLet{\vRA}{((\vEA-\vEB)/(\vEA+\vEB)*0.5+0.5)*4}% 回転
%↑時刻をアフィン変換してtanhしたもの. 4回転している.
\cLet{\vRB}{mod(round(\vRA*1800)/5,360)}% 回転(度数)
%↑0.2度の整数倍に丸めた
%
\newcommand{\cTwo}{% アヒル的"2"
  \makebox(0,0){\tikz[scale=0.24]{\duck[%
    body=blue!50!green,bill=blue!50!green]}}}
\newcommand{\cZero}{% 雪結晶的"0"
  \fontsize{16}{0}\selectfont
  %↓重ね書きはイマイチ
  \makebox(0,1){\color{blue!50!green}\ding{108}}%
  \makebox(1,0){\color{bgcolor}\scalebox{0.8}{\ding{93}}}}
%
\begin{document}%↓なんとpicture環境
\begin{picture}(400,360)(0,-20)
\put(120,\yT){\makebox(0,0){\rotatebox[origin=c]{-\vRB}{%
  \scsnowman[hat=blue!80!green!75!black,
    muffler=blue!50!green,
    arms=green!30!black!50,
    snow=blue!80!green!40,
    scale=12]}}}
\put(280,\yT){\makebox(0,0){\rotatebox[origin=c]{\vRB}{%
  \scsnowman[hat=blue!50!green,
    muffler=blue!80!green!75!black,
    arms=green!30!black!50,
    snow=blue!80!green!40,
    scale=12]}}}
\put(200,260){\makebox(0,0){\textcolor{blue!50!green!60}{%
  \fontsize{48}{0}\sffamily\bfseries\ding{100}}}}
\put(200,200){\makebox(0,0){\textcolor{blue!85!black}{%
  \fontsize{28}{40}\sffamily\bfseries\itshape
  {Happy Snowman's Day}}}}
\put(200,144){\cTwo}
\put(200,112){\cZero}
\put(200,080){\cTwo}
\put(200,048){\cZero}
\end{picture}
\end{document}

f:id:zrbabbler:20200818192052g:plain


  1. TeX言語者向け解説】整数定数として用いるために、制御綴\faStopTicksを値18をもつchardefトークンと定義している。\faAllTicksも同様。

  2. TikZを読み込むとpgfmathが内部で読み込まれるので、この機能を利用するのが手っ取り早いだろう。(そして、scsnowmanパッケージはTikZを内部で読み込む。)

  3. TeX言語者向け解説】\faStopTicksはchardefトークンであるため\number\faStopTicksを1回展開するとその整数値の十進数字列に展開される。pgfmathの式の中で「数字列に展開されるトークン列は数値として扱われるため、この方法で値が読み出せるわけである。ちなみに、chardefトークンそのものはpgfmathの式の中で数値として扱われない。