マクロツイーター

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

例のナベアツ的カウンタの話の補足の話

最近、某キ~タの記事において、「原理的に完全展開可能実装ができないようなカウンタ出力命令」(例えばナベアツとか)を確実に正しく実装するための手順を解説した。その後、別の記事でLuaで実装する手順も扱った。

これらの記事は理解しやすさを最優先したため「何故その手順でうまくいくのか」という理由の説明は一切省略した。本記事では「カウンタ出力命令が正しく動作するための必要条件は何か」という問題を中心にして、以前の記事の手順が「正しい」ことを検証する。

必要な前提知識

  • フツーのTeX言語の知識。
  • e-TeX拡張の\protectedの目的や動作に関する知識。
  • フツーの「LaTeXのカウンタ出力命令の使用」に関する知識。

なお、「脆弱(fragile)」や「頑強(robust)」の概念は、この話題においては却って混乱をもたらすおそれがあるので、本記事では使わない(従って前提知識にもしない)ことにする。

以降の話は手順に従う任意の実装を対象としているが、話を理解しやすくするため、ナベアツ的カウンタ出力命令(\nabeazzstyle)の実装例に登場する制御綴名を仮に用いることにする。またカウンタ名についてはenumiを用いる。

そもそも正しいカウンタ出力命令とは何なのか

まず端的に、カウンタ出力命令の実装が「正しい」(つまりカウンタ出力命令の仕様の要件を満たす)ための「ほぼ必要十分といえる条件」を示す。

  1. (展開限定文脈でない箇所で)\nabeazzstyle{enumi}を実行すると仕様通りに動作する。
    • これはアタリマエである。
  2. \nabeazzstyle{enumi}を一旦保護付完全展開して得られたトークン列を後になって実行すると、やはり仕様通りに動作する。
    • 保護付完全展開とは「\protect\‹制御綴›の完全展開形が\‹制御綴›になる」という付加条件1の下での完全展開である。
    • この際の出力で使われるカウンタ値は「保護付完全展開した時点のもの」でないといけない。つまり保護付完全展開と実行の間でenumiの値を変更しても出力は変わってはいけない。

ここで出てくる「保護付完全展開」とは、まだ\protected等のe-TeX拡張が存在しなかった時代のLaTeXにおいて\protectedと同等の機能を実現するために用意された機構である。もちろんe-TeX無しで\protectedを模倣することは不可能なので、「\protectを機能させたければ生の完全展開ではなく保護付完全展開を使うこと」という制約が課される。もし\protectが全く使われていない状況であれば、保護付完全展開は要するに完全展開と同等であると思ってかまわない。

カウンタ出力命令の実装が与えられたときにそれが「正しい」ものであるかを判断するには、その実装がここで述べた2つの条件を満たすかを確認すればよいことになる。もしイマドキのTeX言語者が保護付完全展開の結果を自分で確認したい場合は\protected@edef(書式は\edefと同じ)を利用してみればよい2

以降では、例の2つの記事で紹介した手順が「正しい」実装をもたらすかについて検証してみる。

例のTeX言語の記事の手順はなぜ動くのか

最初の記事はTeX言語およびexpl3での実装を扱っている。どちらでも同じなのでここではTeX言語版に従うことにする。手順に従った実装が1と2の条件を満たすことを確かめる。

(1)普通に実行したとき

次のようにカウンタ出力命令(nabeazzstyle)を実行したとする。

\nabeazzstyle{enumi}

手順2の定型実装に従うと次のように展開される。

\expanded{\nazstyle@print{\arabic{enumi}}}

これを展開すると\expandedの引数の完全展開形になるはずなのでそれを求めよう。まず先頭の\nazstyle@print(数値出力マクロ)であるが、これは手順1の規約により\protected付きで定義されているので展開されずにそのまま残る。\arabic\protected無しで完全展開可能なので\arabic{enumi}42に展開される。以上をまとめると\expandedの展開結果は以下のようになる。

\nazstyle@print{42}

これは数値出力マクロの仕様に適う「数字表記の整数値」を引数にとった形なので、これを実行すると指定書式の42(つまりアホな42)が出力されることになる。

全体の流れを振り返ると、これはカウンタ出力命令の要求仕様に従った動作になっている。

(2)保護付完全展開した後で実行したとき

次のトークン列について保護付完全展開してみる。先に言っておくとこの話で\protectは出てこないので保護付完全展開は完全展開と同じである。

\nabeazzstyle{enumi}

手順2の規約によりカウンタ出力命令は\NewExpandableDocumentCommandで定義されている。Expandable有りの定義命令の仕様に従い、カウンタ出力命令(\nabeazzstyle)は\protected無しでありまた展開を続けて定義本体のトークン列に至ることが保証される。従って、(1)の場合と同じと同じトークン列に到達する。

\expanded{\nazstyle@print{\arabic{enumi}}}

\expandedは展開可能なので展開され、やはり(1)と同じ結果になる。

\nazstyle@print{42}

\nazstyle@print(数値出力マクロ)は\protected付きなのでそのまま残る。その後には展開不能な文字トークンしかない3ので、これで(保護付)完全展開が完了した。ここで注意したいのは、カウンタ名enumiは残っていなくてその値を表す42に既に置き換わっていることである。

では次に、このトークン列を後のタイミングで実行したとする。すると数値出力マクロ(\nazstyle@print)の仕様に従い、保護付完全展開の時点でのカウンタ値である42が指定書式で出力される。

全体を振り返ると、こちらの動作もカウンタ出力命令の2番目の要件を満たしている。以上より、例の記事の手順で「正しい」カウンタ出力命令が実装できることが判った。

例のLua言語の記事の手順はなぜ動くのか

2つ目の記事はLua言語での実装を扱っている。こちらの記事の手順についても妥当性を検証する。

(1)普通に実行したとき

次のようにカウンタ出力命令(\nabeazzstyle)を実行する。

\nabeazzstyle{enumi}

展開するとこうなる。

\myNabeAzzPrint{\arabic{enumi}}

さらに\myNabeAzzPrint(数値出力命令)を展開する。

\directlua{␣my_nabeazz_print(\inteval{\arabic{enumi}})␣}

\directluaは「引数のトークン列を完全展開して脱トークン化した結果の文字列」をLuaコードとして実行するので、その文字列を求めよう。引数のトークン列中にある最初の展開可能なトークンは\intevalである。

\inteval{~}は簡単にいうと\the\numexpr~\relaxと同じ[^inteval]であり引数の整数式の値の数字表記に展開される。ここでは整数式が\arabic{enumi}なので結局42に展開される。つまり以下のLuaコードが実行される。

␣my_nabeazz_print(42)␣

これはLuaの数値出力関数(my_nabeazz_print)の仕様に適った呼出なので、最終的に指定書式で42が出力される。1の条件を満たしている。

(2)保護付完全展開した後で実行したとき

次のトークン列について(保護付)完全展開してみる。

\nabeazzstyle{enumi}

カウンタ出力命令(\nabeazzstyle)はExpandable有りで定義されているので展開される。

\myNabeAzzPrint{\arabic{enumi}}

数値出力命令(\myNabeAzzPrint)はExpandable無しの定義であり、定義命令の仕様に従うとこれは\protected付きになり、従ってそのまま残る。\arabic{enumi}の部分が展開されて42になる。つまり(保護付)完全展開の結果は次のようになる。

\myNabeAzzPrint{42}

では次に、このトークン列を後に実行したとする。\myNabeAzzPrintが展開される。((1)とは異なるパターンになることに注意。)

\directlua{␣my_nabeazz_print(42)␣}

\directluaの引数は展開不能な文字トークンの列なのでそのまま次の文字列がLuaコードとして実行される。

␣my_nabeazz_print(42)␣

カウンタ出力命令の2番目の要件を満たしている。以上より、例の記事の手順で「正しい」カウンタ出力命令が実装できることが判った。

まとめ

というわけで、TeX言語者🤮の皆さんも、expl3者😵‍💫の皆さんも、Lua者🌝の皆さんも、カウンタ出力命令をつくる際には「正しい」実装になるように心掛けましょう!💁


  1. 要するに\protect\noexpandであると考えればよい。実際、\protected@edef\protect\noexpandにletしてから\edefを実行している。
  2. もちろん保護付完全展開が使われる場面は\protected@edefだけではない。\typeout\addtocontentsでは引数のトークン列を保護付完全展開した結果が書き出される。要するに、相互参照においてauxファイルに書き出されるカウンタ番号は「カウンタ出力命令を保護付完全展開した結果」なのである。
  3. {}の中にあるトークン列は「カウンタ値を十進表記した数字列」だから常に展開不能な文字トークン列となる。

(u)pLaTeXでもイロイロなフォントで☃したい話

例によって人類がアレなせいで、世界が超絶アレなことになっています。もちろん最終的にはエ~アイか何かが人類……(略)……ゆきだるま!⛄

というわけで、人類が未だに存続している情勢の中、今年も辛うじてゆきだるまの日がやってきました!

イロイロなフォントのイロイロな☃の話

さて皆さんご存じの通り、ゆきだるま☃️といえば、「TeXと一切関係がない」ことで有名です。というわけで、本記事ではLaTeXでのゆきだるま☃️出力について考えましょう。

LaTeXで☃️を出力する定番の方法といえばscsnowmanパッケージが有名です。scsnowmanを使うと、イロイロな色のマフラーを着けた素敵な☃️が得られます(素敵😊)

いつものやつ(素敵😊)

しかし世の中には「自分はイロイロなマフラーの☃️ではなく、和文フォントのイロイロなデザインの☃︎を出力したい」という人もきっといるこどでしょう。例えば、Google Fontsで 公開されているフォントに限っても、多種多様なデザインの☃︎が用意されています。

イロイロなフォントの☃(素敵😊)

これらをLaTeXで使うにはどうすればよいでしょうか。

以下では和文文書における和文☃の使用を前提とします。

LuaLaTeXでフツーにイロイロな☃をする話

日本語のLaTeXといえば、イマドキは当然LuaLaTeX(+LuaTeX-ja)ですよね。ご存じの通り、LuaLaTeXでは新しいフォントが手軽に導入できるのでカンタン😍にイロイロな☃が書けます。

LuaTeX-jaにおいて新しい和文フォント1を文書中に導入するにはluatexja-fontspecパッケージの\newjfontfamily命令を利用して「そのフォントに切り替える命令」を定義します。

\newjfontfamily\命令{‹フォント›}

ここで‹フォント›の部分には「フォントファイル名」「フォントファミリ名」「フォント名」の何れか2を指定できます。

LuaLaTeXは本記事の主題ではないので詳細は割愛します。

\newjfontfamilyを利用してLuaTeX-jaでイロイロなフォントの☃を出力する例を示します。

% LuaLaTeX
\documentclass[a4paper]{ltjsarticle}
\usepackage{luatexja-fontspec}% フォントしたい
% 和文フォント命令をイロイロ定義する.
\newjfontfamily\jNoto{NotoSerifJP-Regular.ttf}% Noto Serif Japanese
\newjfontfamily\jKlee{KleeOne-Regular.ttf}% Klee One
\newjfontfamily\jPlex{IBMPlexSansJP-Regular.otf}% IBM Plex Sans JP
\newjfontfamily\jSawa{SawarabiGothic-Regular.ttf}% Sawarabi Gothic
\newjfontfamily\jKais{KaiseiDecol-Regular.ttf}% Kaisei Decol
\newjfontfamily\jYuji{YujiSyuku-Regular.ttf}% Yuji Syuku
\newjfontfamily\jKiwi{KiwiMaru-Regular.ttf}% Kiwi Maru
\newjfontfamily\jDotg{DotGothic16-Regular.ttf}% DotGothic16
\begin{document}
% 和文フォント命令をイロイロ使う.
{\jNoto}は素敵。\par
{\jKlee}も素敵。\par
{\jPlex}もまた素敵。\par
{\jSawa}も同様に素敵。\par
{\jKais}もやっぱり素敵。\par
{\jYuji}もとにかく素敵。\par
{\jKiwi}も例によって素敵。\par
{\jDotg}は多分素敵。\par
\end{document}

出力結果(素敵😊)

カンタンでしたね😍

upLaTeXだがそれでもイロイロな☃をしたい話

このようにイマドキのLaTeXではフォントの扱いはカンタンです。しかし、時には深い事情があって「イマドキのフツーのLaTeX」ができずに、upLaTeX(+dvipdfmx)でイロイロな☃を書きたいこともあるでしょう。

定番の裏技

pLaTeX系でイロイロなフォントを使うための定番の方法として「japanese-otfパッケージの多書体サポートを濫用する」というものが知られています。japanese-otfのdeluxeオプションを有効にすると、和文フォントについて、次の3ファミリ7ウェイトが利用可能になります。

  • 明朝体(\mcfamily)の細字・中字・太字ウェイト
  • ゴシック体(\gtfamily)の中字・太字・極太ウェイト
  • 丸ゴシック体(\mgfamily)の中字ウェイト

といってもこれをフツーに使うのでは結局3書体しか使えないわけですが、実は裏技があります。LaTeXにおいて「明朝体の3ウェイトは全て同じ書体でなければならない」「ゴシック体に割り当てる書体はゴシック体書体でなければならない」という制限は存在しません。このため実際には利用可能な7ウェイトの枠の各々に全く別の好きな書体を割り当てても一向にかまわないのです。

というわけで「japanese-otfの多書体設定を有効にした上で各々のウェイトにpxchfonパッケージで別々のフォントを指定する」という方法でイロイロな☃を出力できます。

% upLaTeX+dvipdfmx
\documentclass[uplatex,dvipdfmx,a4paper]{jsarticle}
\usepackage[deluxe]{otf}% 多書体したい
\usepackage[noalphabet]{pxchfon}% フォント指定したい
% 3ファミリ・3ウェイトの合計7ウェイトが使えるので,
% 各々のウェイトに(ファミリは気にせずに)好きな書体を割り当てる.
\setminchofont{NotoSerifJP-Regular.ttf}% Noto Serif Japanese
\setlightminchofont{KleeOne-Regular.ttf}% Klee One
\setboldminchofont{IBMPlexSansJP-Regular.otf}% IBM Plex Sans JP
\setgothicfont{SawarabiGothic-Regular.ttf}% Sawarabi Gothic
\setboldgothicfont{KaiseiDecol-Regular.ttf}% Kaisei Decol
\setxboldgothicfont{YujiSyuku-Regular.ttf}% Yuji Syuku
\setmarugothicfont{KiwiMaru-Regular.ttf}% Kiwi Maru
\begin{document}
% 例えば"Yuji Syuku"は"\setxboldgothicfont"、つまり"ゴシック・極太"に
% 割り当てたので, "\gtfamily\ebseries"でこのフォントに切り替えられる.
{\mcfamily\mdseries}は素敵。\par           % \setminchofont
{\mcfamily\ltseries}も素敵。\par           % \setlightminchofont
{\mcfamily\bfseries}もまた素敵。\par       % \setboldminchofont
{\gtfamily\mdseries}も同様に素敵。\par     % \setgothicfont
{\gtfamily\bfseries}もやっぱり素敵。\par   % \setboldgothicfont
{\gtfamily\ebseries}もとにかく素敵。\par   % \setxboldgothicfont
{\mgfamily\mdseries}も例によって素敵。\par % \setmarugothicfont
\end{document}

出力結果(素敵😊)

めでたしめでたし……アレレ? 1つ足りませんね……🤔

“DotGothic16”の☃(多分素敵🤔)

そもそもjapanese-otfのdeluxeモードで使える枠の数は7個です。これではどう頑張っても高々7種類の☃しか使えないのは明らかです。

今年は令和☃年なので「☃個出力したい」というのも当然の要求としてあるでしょう。どうにかして8種類以上の和文フォントを使うことはできないでしょうか。

もっとアレな裏技

そもそもjapanese-otfのdeluxeモードが実現できていることからわかるように、(u)pLaTeXで使える和文フォントの個数には本来は制限はありません。問題なのは、新たな和文フォントを導入するためには「pTeX系エンジンの和文フォントの仕組み」についての知識が必要でこれを説明するのが極めて困難3ということです。

正攻法の説明を書こうとすると永遠に終わらなくなってその結果🙃が埋められる事態に発展するおそれが高いため、ここでは代わりにチートを駆使することにします。つまり「☃を出力する」という目的に特化して「和文フォント導入の思い切り簡略化した手順」を示すという方針をとります。

手始めに「DotGothic16」フォントを導入してみましょう。和文フォントを導入するにはどうしてもTFMファイルが必要なので用意します。といってもTFMファイルについての知識(「TFMファイルとは何か」など)は一切要りません。既存のものをコピーすることで済ませます。

まず、upTeXの標準のTFMファイルであるuprml-h.tfmの場所をkpsewhichコマンドを使って特定します。

kpsewhich uprml-h.tfm

パス名4が返るので、そのファイルをカレントディレクトリにniceA-h.tfmという名前でコピーしましょう。

これでTFMファイルniceA-hが用意できたので、次にこのTFMにDotGothic16マップします。これにはpxchfonパッケージの\usefontmapline命令を利用します。今の場合は次のようになります。

\usepackage[noalphabet]{pxchfon}
% 書式は"\usefontmapline{‹TFM名› unicode ‹フォントファイル名›}"
\usefontmapline{niceA-h unicode DotGothic16-Regular.ttf}
今のpxchfonはオプション無しで読み込むと警告が出るので`noalphabet`を指定しています。とにかく(どんなオプション指定でも)pxchfonが読み込まれれば十分なので、pxchfonが既に読込済の状況ならそのままでかまいません。

最後に「niceA-hというTFMの和文フォントを呼び出すためのLaTeXの命令」(ここでは\jDotgとする)を定義します。bxnewfontパッケージ\newfontx*命令5を利用すれば簡単に実現できます。

\usepackage{bxnewfont}
\newfontjascale{*}
% 書式は \newfontx*\命令{‹TFM名›}
\newfontx*\jDotg{niceA-h}

これで\jDotgを「和文フォントをDotGothic16に切り替える命令」として定義できました。今までのコードをまとめた例を示します。

% upLaTeX+dvipdfmx
\documentclass[uplatex,dvipdfmx,a4paper]{jsarticle}
\usepackage[noalphabet]{pxchfon}% "\usefontmapline"したい
\usepackage{bxnewfont}% "\newfontx"したい
\newfontjascale{*}% とにかくコレを実行
% "nice-A"を使うためのファミリ命令"\jDotg"を定義する
\newfontx*\jDotg{niceA-h}
% "nice-A"に"DotGothic16"を紐付ける
\usefontmapline{niceA-h unicode DotGothic16-Regular.ttf}
\begin{document}
% "\jDotg"で和文フォントが"DotGothic16"に切り替わる.
{\jDotg 本日は☃天なり}
\end{document}

出力結果(多分素敵🙃)
うまくいきました😃

ここまでできれば、あとはこの作業を繰り返していくらでも和文フォントを増やせます。とりあえず8種類の☃を出力する文書をつくりましょう。8個の和文フォントを使うにはTFMも8個必要になるので、先ほどのniceA-h.tfmをコピーしてniceB-h.tfmからniceH-h.tfmまでの7つのファイルを新たに用意します。その上で次のようなソースファイルを作りました。

% upLaTeX+dvipdfmx
\documentclass[uplatex,dvipdfmx,a4paper]{jsarticle}
\usepackage[noalphabet]{pxchfon}% "\usefontmapline"したい
\usepackage{bxnewfont}% "\newfontx"したい"
\newfontjascale{*}% とにかくコレを実行
%% \NewJaFont\命令{TFM名}{フォントファイル名}
\newcommand*\NewJaFont[3]{%
  \newfontx*#1{#2}%
  \usefontmapline{#2 unicode #3}%
}
% 和文フォント命令をイロイロ定義する.
\NewJaFont\jNoto{niceA-h}{NotoSerifJP-Regular.ttf}
\NewJaFont\jKlee{niceB-h}{KleeOne-Regular.ttf}
\NewJaFont\jPlex{niceC-h}{IBMPlexSansJP-Regular.otf}
\NewJaFont\jSawa{niceD-h}{SawarabiGothic-Regular.ttf}
\NewJaFont\jKais{niceE-h}{KaiseiDecol-Regular.ttf}
\NewJaFont\jYuji{niceF-h}{YujiSyuku-Regular.ttf}
\NewJaFont\jKiwi{niceG-h}{KiwiMaru-Regular.ttf}
\NewJaFont\jDotg{niceH-h}{DotGothic16-Regular.ttf}
\begin{document}
% 和文フォント命令をイロイロ使う.
{\jNoto}は素敵。\par
{\jKlee}も素敵。\par
{\jPlex}もまた素敵。\par
{\jSawa}も同様に素敵。\par
{\jKais}もやっぱり素敵。\par
{\jYuji}もとにかく素敵。\par
{\jKiwi}も例によって素敵。\par
{\jDotg}は多分素敵。\par
\end{document}

出力結果(素敵😊)

8種類の☃が無事出力できました🙂

補足:TFMファイルの配置場所

ここではnice-*.tfmのTFMファイルをカレントに置いたまま作業しましたが、もし本記事の方法を実用したい場合はこのままでは邪魔でしょう。以下の方法でカレントに置くのを避けられます。

  • 本記事の方法を常用する予定がある場合は、これらのTFMファイルを通常の手順に従ってローカルのTEXMFツリー6インストールすればよいでしょう。
  • 特定の文書でのみ使いたい場合はTFMFONTSという環境変数に.//;という値を設定します。これでTFMを読み込む際にサブディレクトリが探索されるようになります。(過去の記事で用いたのと同じ原理です。)

pLaTeXだがどうにかしてイロイロな☃をしたい話

もしかしたら、やんごとなき事情があってpLaTeX(+dvipdfmx)という骨董品でイロイロな☃を使いたいという事態が生じるかもしれません。そもそもpLaTeXでは☃の文字を出すだけで大変(japanese-otfが必要になる7)なのですが、現在のpLaTeXでは「upLaTeXのフォントを使う」というさらなる裏技を使って8種類の☃を出力できます。

もっともっとアレな裏技

pLaTeXとupLaTeXでは和文TFMの文字コード(エンコーディング)が異なる(pLaTeXはJISエンコーディング、upLaTeXはUnicode)ので基本的に「upLaTeXのTFMをpLaTeXで使う」ことはできません。

実際に前節の「upLaTeXの裏技」で作成したTFM(例えば“Kaisei Decol”にマップしたとする)をpLaTeXで無理やり使おうとすると、TeXのタイプセットは通りますが、その先の処理で異常になります(dvipdfmxがエラーになるか、または出力が文字化けになる)。

そもそもpLaTeXでは“☃”は和文扱いできないので、まずは“アレ”で試すことにします。

(失敗例)
% pLaTeX+dvipdfmx
\documentclass[platex,dvipdfmx,a4paper]{jsarticle}
% 和文フォント命令定義の部分はupLaTeXと全く同じにする.
\usepackage[noalphabet]{pxchfon}
\usepackage{bxnewfont}
\newfontjascale{*}
\newfontx*\jKais{niceA-h}
% "Kaisei Decol"にマップした
\usefontmapline{niceA-h unicode KaiseiDecol-Regular.ttf}
\begin{document}
% pLaTeX"なので☃"の代わりに"アレ"で試す
{\TeX}{\jKais アレ}%→dvipdfmxがエラーになる😭
\end{document}

ここでもう少し考えてみましょう。upLaTeXのTFMはUnicodeなんだから、Unicodeの符号値を指定すればいいはずです。LaTeXには「文字コードで文字を入力する」ための\symbol命令があるので、これを使ってUnicodeの符号値8を入力してみましょう。

(失敗例)
% pLaTeX+dvipdfmx
\documentclass[platex,dvipdfmx,a4paper]{jsarticle}
%…………プレアンブルは先と同じ…………
\begin{document}
% "ア"はU+30A2, "レ"はU+30EC.
{\TeX}{\jKais \symbol{"30A2}\symbol{"30EC}}%→やはりdvipdfmxがエラーになる😭
\end{document}

残念ながら、pLaTeXの文字コード関連の処理はもっともっと複雑なので、この単純な解決策は通用しません(再びdvipdfmxがエラーになります)。しかし実はここで究極の裏技が存在します。なんと「符号値に0x110000を加えておく」という細工を行うことで正常な出力が得られるのです😲

% pLaTeX+dvipdfmx
\documentclass[platex,dvipdfmx,a4paper]{jsarticle}
%…………プレアンブルは先と同じ…………
\begin{document}
% 0x110000を加算すると成功する!
{\TeX}{\jKais \symbol{"1130A2}\symbol{"1130EC}}\end{document}

出力結果(非素敵😐)

無事にKaisei Decolの「アレ」が出ました。ここで使っているのは符号値であるため、同じ仕組を利用してKaisei Decolの☃も出せるはずです。☃の符号値はU+2603なので0x112603を指定します。

% pLaTeX+dvipdfmx
\documentclass[platex,dvipdfmx,a4paper]{jsarticle}
\usepackage[noalphabet]{pxchfon}
\usepackage{bxnewfont}
\newfontjascale{*}
\newfontx*\jKais{niceA-h}
\usefontmapline{niceA-h unicode KaiseiDecol-Regular.ttf}
\begin{document}
% "☃"の符号位置はU+2603なので, 文字コードとして0x112603を指定する.
吾輩は{\jKais \symbol{"112603}}である。
\end{document}

出力結果(素敵😊)

うまくいきました! それではこの「究極の裏技」を利用して8種類の☃を出力してみましょう。

% pLaTeX+dvipdfmx
\documentclass[platex,dvipdfmx,a4paper]{jsarticle}
\usepackage[noalphabet]{pxchfon}
\usepackage{bxnewfont}
\newfontjascale{*}
%% \NewJaFont\命令{TFM名}{フォントファイル名}
\newcommand*\myNewJaFont[3]{%
  \newfontx*#1{#2}%
  \usefontmapline{#2 unicode #3}%
}
% "\myNewJaFont"で定義したフォントで"☃"を出力する.
\newcommand*\mySnowman{\symbol{"112603}}
% 和文フォント命令をイロイロ定義する.
\myNewJaFont\jNoto{niceA-h}{NotoSerifJP-Regular.ttf}
\myNewJaFont\jKlee{niceB-h}{KleeOne-Regular.ttf}
\myNewJaFont\jPlex{niceC-h}{IBMPlexSansJP-Regular.otf}
\myNewJaFont\jSawa{niceD-h}{SawarabiGothic-Regular.ttf}
\myNewJaFont\jKais{niceE-h}{KaiseiDecol-Regular.ttf}
\myNewJaFont\jYuji{niceF-h}{YujiSyuku-Regular.ttf}
\myNewJaFont\jKiwi{niceG-h}{KiwiMaru-Regular.ttf}
\myNewJaFont\jDotg{niceH-h}{DotGothic16-Regular.ttf}
\begin{document}
% 和文フォント命令をイロイロ使う.
% ※"☃"ではなく"\mySnowman"とすることに注意.
{\jNoto \mySnowman}は素敵。\par
{\jKlee \mySnowman}も素敵。\par
{\jPlex \mySnowman}もまた素敵。\par
{\jSawa \mySnowman}も同様に素敵。\par
{\jKais \mySnowman}もやっぱり素敵。\par
{\jYuji \mySnowman}もとにかく素敵。\par
{\jKiwi \mySnowman}も例によって素敵。\par
{\jDotg \mySnowman}は多分素敵。\par
\end{document}

出力結果(素敵😊)

トッテモ素敵😊😊😊

補足:裏技の限界

本記事の手順を利用した場合、新しいフォントの文字は常に「そのまま全角幅」で出力9されます。つまり和文組版の規則に従った調整が行われずに常に「通常の漢字」と同様の出力になるということです。

従って、この手順は☃(U+2603)に限らず、⛄︎(U+26C4)や⛇(U+26C7)等の他の本質的文字あるいは漢字や仮名などの(非本質的)文字について「特定の文字だけ他のフォントから借りる」という場面にも応用が利きます。一方で和文組版の調整の範囲外となるため、ここで定義したフォントを本文組のために使用するのは不適切となります。

さらに、いつもの(u)pLaTeXの和文フォント使用に関する制限にも注意が必要です。すなわち、正常に使用できるのは「元々全角幅である文字」に限られます。

まとめ


(ソースファイル)

というわけで、イロイロなフォントでフツーに☃したい場合はLuaLaTeXしましょう!💁(せやな🙂)


  1. 欧文フォントを導入したい場合にはfontspecパッケージの\newfontfamily命令を利用します。
  2. これらのフォントの指定方法のうちどれが有効であるかは環境によって差異があって結構複雑です。取りあえず「OSにインストールされたフォント」であればどの方法も使えるはずです。
  3. キホンの知識の説明に留めるにしても薄い本が1冊書けるだけの分量があります。
  4. 例えば/usr/local/texlive/2026/texmf-dist/fonts/tfm/uptex-fonts/jis/uprml-h.tfmのような値が返ります。
  5. 大昔のLaTeX 2.09では新しいフォントをLaTeXのシステムに導入する仕組(今のNFSS)がなく、どうしても新しいフォント(TFM)をLaTeXに持ち込みたい場合の最後の手段として\newfontという「TFMをLaTeX命令に転換する命令」(中身は\fontの薄いラッパー)が用意されていました。\newfontx*命令はこれの拡張版に相当していて、NFSSと整合した動作をする(「現在のファミリを変更する宣言型命令」として定義される)ように改良されています。\newfontjascale{*}は「\newfontx*で和文フォント命令を定義する際に現在の標準の和文スケール値が適用されるようにする」ための設定です。
  6. TFMファイルのインストール先のパスは$TEXMF/fonts/tfm以下の任意のディレクトリになります。ここで$TEXMFの部分はユーザ用ツリーの場合はTEXMFHOME、システム用ツリーの場合はTEXMFLOCALのKpathsea変数の値を指します。
  7. その理由は、JIS X 0208という大昔の日本語文字コードに「☃」の文字が収録されていないからです。
  8. (La)TeXで整数値を16進表記で入力する場合には接頭辞として"を付けます。例えば"2Aは42を表します。
  9. ただし半角カタカナは「そのまま半角幅」で出力されます。

問題:ナベアツ総和方程式(という名のテスト)

というわけで、早速自分も新しいTeX記法を試してみた。ネタは何でもよかったが、最近ナベアツがにわかに流行っているようなので久しぶりにナベアツしてみた。

問題文

高橋君は、最近にわかにナベアツが流行っていることを知り、特定の範囲にあるナベアツ数の総和に興味を持ちました。ただし「ナベアツ数」とは「3 の倍数または(十進表記で)3 のつく整数」を指します。

与えられた正整数 d に対して、1 から 10^d の範囲にあるナベアツ数の総和を求めてください。

ただし、答えは非常に大きくなる可能性があるので、998244353 で割った余りを出力してください。

制約

  • 1 \leq d \leq 10^5

解説

よく知られているように(えっ🙃)求める総和 S は次の式で求められる。


S = \begin{cases}
18 & \text{(}d = 1~\text{のとき)}\\
\dfrac{(10^d - 1)(81\cdot10^d - 56\cdot9^d)}{162} &
  \text{(それ以外)}
\end{cases}

従って、S998244353 で割った余りを求めるプログラムは以下のように書ける。

M = 998244353
for d in range(1, 100001):
    print(
        18 if d == 1
        else (
            (pow(10, d, M) - 1) *
            (81 * pow(10, d, M) - 56 * pow(9, d, M)) *
            pow(162, -1, M) # 逆元
        ) % M
    )

(めでたしめでたし😊)

bxcoloremojiがもっとdvisvgmできるようになった話

最近、自作のパッケージのdvisvgmへの対応を進めている。既にpxchfonパッケージbxtexlogoパッケージについて記事を書いた。本記事ではbxcoloremojiパッケージの最近(v1.2)の改修におけるdvisvgm対応の内容について説明する。

前提知識

  • dvisvgmのフツーの使い方。
  • bxcoloremojiパッケージのフツー(dvisvgm以外)の使い方。

dvisvgmでフツーに使えるようになった話

メジャーなワークフローにおいてはbxcoloremojiパッケージは「フツーに読み込むだけで取りあえず1使える」状態になっている。1.0版以降のbxcoloremojiは絵文字画像出力のためにtwemojisパッケージを利用しているが、twemojisの読込は自動で行われるので特にそれを気にする必要はない。LuaLaTeXなどのPDF出力のエンジンにおいては本当に何も気にせずただ読み込むだけでよい。

[example1.tex]
% LuaLaTeX
\documentclass[a4paper]{ltjsarticle}
\usepackage{bxcoloremoji}% 読み込むだけ!
\begin{document}
素敵\coloremoji{}
\end{document}

「DVI出力エンジン+dvipdfmx」のワークフローの場合も、「常にドライバオプションをグローバルに指定する」というイマドキのLaTeXの常識に従っていれば、bxcoloremojiを使うにあたって特に気にする点はない。

[example2.tex]
% upLaTeX+dvipdfmx
% イマドキのLaTeXではドライバオプションは無条件で付ける
\documentclass[uplatex,dvipdfmx,a4paper]{jsarticle}
\usepackage{bxcoloremoji}% 読み込むだけ!
\begin{document}
やっぱり素敵\coloremoji{}
\end{document}

同様に考えると「DVI出力エンジン+dvisvgm」の場合も「ドライバオプションさえ忘れなければあとは何も気にしなくてよい」となってほしい。

pxchfonパッケージの記事でも述べた通り、日本語LaTeXとdvisvgmの併用においては(pxchfonを使わない限りは)pLaTeXの使用が「無難」なので、本記事ではdvisvgm併用の例ではpLaTeXを使用する。この場合、dvisvgm実行時に--fontmap=kanjix.mapのオプション指定が必要である。

[example3.tex]
% pLaTeX+dvisvgm
\documentclass[platex,dvisvgm,a4paper]{jsarticle}% ドライバ指定したよ
\usepackage{bxcoloremoji}% 読み込むだけ!
\pagestyle{empty}% 画像なのでページ番号は不要
\begin{document}
例によって素敵\coloremoji{}
\end{document}

しかし旧版のbxcoloremojiではdvisvgmは「その他大勢のDVIドライバの一つ」という扱いで、パッケージの動作の前提となる設定を自分で行う必要があった。このため先の単純な設定では次のようなエラーが発生する。

! Package bxcoloremoji Error: Package 'graphicx' is not loaded.

エラーに対処するには(ドライバ指定はできているので)単純にgraphicxパッケージを前もって読み込めばよいのだが、すると今度はtwemojisパッケージの読込が要求される。twemojisも追加すると正常に動作するが、実はこれでも最適ではない。bxcoloremojiはdvipdfmxドライバの場合に画像の挿入を高速化するため「bbオプション設定2」という手法を利用している。この手法はdvisvgmでも適用可能であるが、既定では無効になっているので、画像挿入に余計な時間がかかっている3のである。最適にするにはbbparam=trueというオプションを指定する必要がある。

つまり旧版のbxcoloremojiをdvisvgmで使おうとすると以下のように書く必要があった。これは単純に面倒なだけでなくこの「正解」に行き着くのが難しいという問題もあった。

[example4.tex]
% pLaTeX+dvisvgm
\documentclass[platex,dvisvgm,a4paper]{jsarticle}
\usepackage{graphicx,twemojis}% 別のパッケージ読込が必要でした
\usepackage[bbparam=true]{bxcoloremoji}% 'bbparam'指定も必要でした
\pagestyle{empty}
\begin{document}
例によって素敵\coloremoji{}
\end{document}

新版のbxcoloremojiではこの点を改善して、dvisvgmのグローバルオプションが指定されている場合に自動的に「dvisvgm用の最適な設定」を行うようにした(dvipdfmxと同じ処置)。従って、先ほどの単純な設定のexample3.texが正常に動作するようになる。

カスタムファミリの使用を指定した場合はtwemojisを使わずに直接graphicxパッケージの\includegraphics命令で画像を挿入する形になるが、この場合も「何も気にせずに」dvisvgm上で使える。以下に示すのはbxcoloremoji-oldstdバンドル4で提供されるカスタムファミリのtwemoji-pdfを利用した例である。

[example5.tex]
% pLaTeX+dvisvgm
\documentclass[platex,dvisvgm,a4paper]{jsarticle}
\usepackage[family=twemoji-pdf]{bxcoloremoji}% カスタムファミリ指定
\pagestyle{empty}
\begin{document}
相変わらず素敵\coloremoji{}
\end{document}

相変わらず⛄️は素敵😊

dvisvgmでPNGできるようになった話

bxcoloremojiではPNG形式の画像もカスタムファミリとして使用できるが、dvisvgm上で使う場合では少し注意が必要である。

dvisvgm上でPDF形式の画像を\includegraphics命令で挿入5した場合、その画像データはSVGのコードに変換された上で出力のSVGファイル中に埋め込まれる。例えば先ほどの出力のexample5.svgの中を見ると以下のように絵文字の描画データが埋め込まれている。

<text class='f1' x='9.148144' y='53.004663'>相変わらず素敵 </text>
<g transform='matrix(.340741 0 0 .340741 73.63453 44.910091)'>
<path d='M21 19.5C21 23.641 17.641 27 13.5 27S6 23.641 6 19.5S9.359 12 13.5 12S21 15.359 21 19.5Z' fill='#e1e8ed'/>
<path d='M9 8.25C9 5.766 11.016 3.75 13.5 3.75S18 5.766 18 8.25S15.984 12.75 13.5 12.75S9 10.734 9 8.25Z' fill='#e1e8ed'/>
<path d='M17.25 4.5C17.25 5.328 16.578 6 15.75 6H11.25C10.422 6 9.75 5.328 9.75 4.5V1.5C9.75 .672 10.422 0 11.25 0H15.75C16.578 0 17.25 .672 17.25 1.5Z' fill='#414042'/>
<path d='M18.75 5.25C18.75 5.664 18.414 6 18 6H9C8.586 6 8.25 5.664 8.25 5.25S8.586 4.5 9 4.5H18C18.414 4.5 18.75 4.836 18.75 5.25Z' fill='#231f20'/>
<path d='M16.875 11.25H10.125C9.504 11.25 9 11.754 9 12.375C9 12.863 9.312 13.277 9.75 13.43V18.75C9.75 19.164 10.086 19.5 10.5 19.5H11.25C11.664 19.5 12 19.164 12 18.75V13.5H16.875C17.496 13.5 18 12.996 18 12.375S17.496 11.25 16.875 11.25Z' fill='#dd2e44'/>

これに対して、PNGなどのビットマップ画像\includegraphicsで挿入した場合は、出力のSVGファイルには当該の画像ファイルへの参照(HTMLのimg要素のようなもの)が記される。画像を挿入したい場合、通常はそのファイルは「手許」(文書ファイル群があるディレクトリ)に置かれていて、この場合は参照は機能するだろう。例えば「絵文字『🙃』のPNG画像がカレントディレクトリにzannen.pngとして存在する」という前提で以下のLaTeX文書をSVGに変換してみる。

[example6.tex]
% pLaTeX+dvisvgm
\documentclass[platex,dvisvgm,a4paper]{jsarticle}
\usepackage{graphicx}% bxcolormoejiしない
\pagestyle{empty}
\begin{document}
ざんねん
% 普通に手許の画像ファイルを挿入した
\includegraphics[width=1zw]{zannen.png}
\end{document}

出力のSVGファイルに記されているのは描画データそのものでなくzannen.pngというファイルへの参照である。

<text class='f1' x='9.148144' y='53.004663'>ざんねん</text>
<g transform='translate(45.9975,43.792687)'>
<svg height='9.21199' overflow='visible' viewBox='0 0 72 72' width='9.2123'>
<image height='72' width='72' xlink:href='zannen.png'/>
</svg>
</g>

今のような通常のケースでは、少なくとも「出力のSVGファイルと画像ファイルを一緒に取り扱う」ことに留意していれば問題ない。しかしbxcoloremojiでPNG画像を扱う場合は、画像ファイルが「手許」にないので、画像が参照になるのは甚だ都合が悪い。実際に旧版のbxcoloremojiでPNG形式のカスタムファミリ(twemoji-png)を使おうとすると問題が起こる。

[example7.tex]
% pLaTeX+dvisvgm
\documentclass[platex,dvisvgm,a4paper]{jsarticle}
\usepackage{graphicx,twemojis}% bxcoloremojiが古いのでコレが必要
\usepackage[family=twemoji-png]{bxcoloremoji}% PNGのカスタムファミリ
\pagestyle{empty}
\begin{document}
結局のところ素敵\coloremoji{}
\end{document}

この場合も先と同じ理屈に従って、絵文字の画像ファイルへの参照が記録される。

<image height='72' width='72' xlink:href='twemoji-png/twemoji-26C4.png'/>

しかしSVGファイルを扱うソフトウェアはTeXと無関係なものであるため、このパスを基にして「TeX一式のインストールの中にある画像ファイル」を見つけることが当然できない。結局、絵文字の表示は失敗してしまう。

実は、dvisvgmには「ビットマップ画像を参照ではなくデータ埋込6として扱う」ためのオプション--embed-bitmapsが用意されている。ところが残念なことに、今のgraphicxパッケージを前提にする限りはこのオプションは効果をもたない。何故かというと、graphicxパッケージのdvisvgm用ドライバの現在の実装では、ビットマップ画像を挿入するのに「dvisvgmのビットマップ画像挿入用命令7」を利用していないからである。画像挿入の処理はXMLコードを直接吐き出す実装であるため、--embed-bitmapsの影響を受けないのである。

この問題に対処するため、新版のbxcoloremojiでは「dvisvgmドライバでビットマップ画像を挿入する」場合に\includegraphicsの処理を一部修正8して「dvisvgmのビットマップ画像挿入用命令」を使うようにした。これによりdvisvgmの--embed-bitmapsオプションと併用することで「絵文字画像を埋め込んだSVGファイル」が得られるようになる。

新版のbxcoloremojiでtwemoji-pngカスタムファミリを使う例を示す。

[example8.tex]
% pLaTeX+dvisvgm
\documentclass[platex,dvisvgm,a4paper]{jsarticle}
\usepackage[family=twemoji-png]{bxcoloremoji}% PNGのカスタムファミリ
\pagestyle{empty}
\begin{document}
結局のところ素敵\coloremoji{}
\end{document}

これを次の手順で変換すると「絵文字画像を埋め込んだSVGファイル」が得られる。

platex example8.tex
dvisvgm --embed-bitmaps --fontmap=kanjix.map --font-format=woff2 example8.dvi

結局のところ⛄️は素敵😊

まとめ

新版のbxcoloremojiパッケージをdvisvgmで使う方法は以下の通り。

  • 基本的に何も気にせずにフツーに読み込めばOK。
  • PNG形式のカスタムファミリを使う場合はdvisvgmに--embed-bitmapsオプションを付ける。

  1. もちろん「好みに応じてオプションを調整する」余地はあるが、既定の設定でも目的は達成できる程度には機能する。
  2. 現在のgraphicxパッケージのdvipdfmx上の動作では画像の外見の大きさの情報(バウンディングボックスの値)を「自動で外部コマンド(extractbb)を実行する」ことで取得しているが、この処理は外部コマンド起動のせいでかなり時間を要する。バウンディングボックスの値を\includegraphicsbbオプションに指定するとこの処理が省略されるので、所要時間を削減できる。
  3. つまり高速化されていないので「ユーザが用意した画像を\includegraphicsでフツーに挿入する」のと同様に遅い、ということ。
  4. まだtwemojisパッケージへの移譲をしてなかった0.x版時代に標準として用いられていた画像セットをカスタムファミリの形で提供したもの。中身は0.x版の画像と全く同じなので、0.x版の画像のインストールがそのまま残っているならばこのバンドルをインストールしたのと同様にカスタムファミリ(twemoji-pdftwemoji-png)が使える。
  5. もちろん、bxcoloremojiでPDF形式のカスタムファミリを使用した場合も同じである。さらに、twemojisパッケージ自体がPDF形式の画像を利用しているので、bxcoloremojiで既定のtwemojisモードを利用した場合も同じ話になる。
  6. これは画像ファイルの参照先をdata URL(data:imgae/png;…)にすることで実現している。
  7. dvisvgm:imgというdvisvgm専用のspecial命令である。
  8. もちろん「bxcoloremojiで絵文字画像を挿入する」場合にだけ修正が適用される。通常の\includegraphics命令の動作には影響しない。

もっと新しくなったbxtexlogoの話(v0.8)

前回のbxtexlogoの記事は0.4版のときのものだったので、それより後の改修点について解説する。

ロゴがもっと増えた件

追加されたやつ

※上の4つ(OpTeXCSTUGHiTeXTeXXeT)は**による一括インポートの対象となる。それ以外は一括インポートの対象外で、個別にインポートする必要がある。
1TeXOneTeXと等価である。\bxtexlogo命令で使う用途で用意した。
XeT以下の6つはロゴを構成する部品である。loweは「LaTeX2e」のロゴに現れる「下がったε」の部分。
YukidarumaTeXは素敵っぽいが何を指すかは不明である(ざんねん🙃)

さっそく新しいロゴを使ってみた文書の例。

% upLaTeX+dvipdfmx文書
\documentclass[uplatex,dvipdfmx,a4paper]{jsarticle}
\usepackage{graphicx,bxtexlogo}
% "Prote"等は一括インポートの対象外なので個別に指定する
\bxtexlogoimport{*,**,Prote,lowe,One,Yukidaruma}
\begin{document}
\begin{itemize}
\item 現在の{\HiTeX}{\eTeX}拡張をもつ。\\{\Prote}の実装を利用しているらしい。\\
  ※ただし{\TeXXeT}拡張は除外されている。
\item {\LaTeX}3{\lowe}(非存在)
\item sc{\One}Typstしましょう!{\Yukidaruma}
\end{itemize}
\end{document}

hologoのロゴが全部使える件

「一括インポート対象外のロゴ」という概念を導入したついでに、今まで省いていたものも含めて全てのhologo提供のロゴをbxtexlogoでも使用可能にした。以下に示すのはその中の一部である。

追加されたhologoのやつ(抜粋)

新たに追加したものは一括インポート(***)の対象外なので、個別にインポートするか、または後述の\bxtexlogo命令を1使う必要がある。

% upLaTeX+dvipdfmx文書
\documentclass[uplatex,dvipdfmx,a4paper]{jsarticle}
\usepackage{graphicx,bxtexlogo}
% やはり個別にインポートする
\bxtexlogoimport{La,PiC}
\newcommand\+{\hspace{0pt}}% 和欧文間空白を消すトリック
\begin{document}
\begin{itemize}
\item アレ/{\La}アレ Advent Calendar
  % ちなみに"\TeX"は(カーネル定義済なので)常に使える
\item あたま{\TeX}\+ァ\+{\TeX}\+ァ さえて{\PiC}\+ァ\+{\PiC}\+ァ(違う)
\end{itemize}
\end{document}

\bxtexlogo: インポートせずに使える件

\hologo{‹名前›}と書く必要のあるhologoのロゴを単一命令にして使いやすくする」のが本パッケージの当初の目的であったが、本パッケージ独自のロゴが増えてきたので、逆に「独自の(稀に使う)ロゴをわざわざインポートせずに\hologoみたいな命令で直接書きたい」という要望が想定できる。そこで、名前を引数に指定してロゴを出力する命令である\bxtexlogo命令を用意した。

※命令としてのインポートの有無にかかわらず\bxtexlogo命令は常に使用可能である。

% upLaTeX+dvipdfmx文書
\documentclass[uplatex,dvipdfmx,a4paper]{jsarticle}
\usepackage{graphicx,bxtexlogo}
\bxtexlogoimport{LuaTeX}% "\LuaTeX"をインポートした
\begin{document}
{\LuaTeX}とか% インポートしたので使える
\bxtexlogo{LuaTeX}とか% "\bxtexlogo"は無条件に使える
\bxtexlogo{LuahbTeX}とか。% やはり無条件に使える
\end{document}

今回新たに追加したロゴの中には1TeXや(hologoの)(La)TeXのように非英字を含む名前のものがある。これらはインポートして命令にする使い方はできない2が、\bxtexlogoで使うことが想定されている。

% upLaTeX+dvipdfmx文書
\documentclass[uplatex,dvipdfmx,a4paper]{jsarticle}
\usepackage{graphicx,bxtexlogo}
\begin{document}
\bxtexlogo{OneTeX}とか
\bxtexlogo{1TeX}とか% 数字が入っても問題無し
\bxtexlogo{(La)TeX}とか。% hologo提供のロゴ("TeXLaTeX"と同じ)
\end{document}

dvipsとかdvisvgmでマトモに1TeXできる話

一般的に、TeX関連のロゴを斜体(イタリックも含む)の書体の中で用いる場合はロゴの文字も斜体で書く。しかしロゴで使われる特殊な記号の中には斜体のフォントが用意できないものもあり、本パッケージ提供のロゴでは、そういう部品について「斜体変形をかけた文字」(合成斜体)が使われている。例えば、「1TeX」(OneTeX)の「1」や「スヤァTeX」(SuyahTeX)の「スヤァ」の部分が該当する。

% pdfLaTeX文書
\documentclass[a4paper]{article}
\usepackage{graphicx,bxtexlogo}% ここではgraphicxは必須
\bxtexlogoimport{OneTeX,SuyahTeX,YukidarumaTeX}
\begin{document}
\itshape % 全体をイタリックに印字
\begin{itemize}
\item I like {\OneTeX} better than {\SuyahTeX}.
\item Do you know what {\YukidarumaTeX} is?
\end{itemize}
\end{document}

※「合成斜体」を使ったロゴを出力する場合はgraphicxパッケージの読込が必要である。これを忘れると警告が出る。

ところが諸般の事情(=手抜き🙃)のため、従来はこの「合成斜体」の処理がpdfTeX系とdvipdfmx系のドライバにおいてしか実装されてなかった。このため、例えばdvisvgmを使った場合は「合成斜体」が無効になりロゴの外見がイマイチなことになっていた。

0.8版においてdvipsおよびdvisvgmについても「合成斜体」の処理が実装されたので、それを使ったロゴの出力がマトモになる。

dvisvgmを使う場合は、(dvipdfmxの時と同じく)グローバルオプションにdvisvgmを指定する。これでgraphicxのドライバがdvisvgm用になり、bxtexlogoはgraphicxの設定に追随するので正しくdvisvgm用の処理が使われるようになる。

% 欧文LaTeX+dvisvgm文書
\documentclass[dvisvgm,a4paper]{article}% ドライバオプション指定
\usepackage{graphicx,bxtexlogo}% ここではgraphicxは必須
\bxtexlogoimport{OneTeX,SuyahTeX,YukidarumaTeX}
\begin{document}
\itshape % 全体をイタリックに印字
\begin{itemize}
\item I like {\OneTeX} better than {\SuyahTeX}.
\item Do you know what {\YukidarumaTeX} is?
\end{itemize}
\end{document}

※先のpdfLaTeX用のソースコードと異なるのはドライバオプション(dvisvgm)の追加の部分だけである。

余談であるが、Yukidarumaの“ロゴ”は、本来は(実態が不明な)「☃TeX」のロゴの部品として用意されているが、ロゴとは無関係に「☃」の文字を出す用途に使える。「斜体に対応した☃の文字」を出す手段は貴重であるので、本質的な語句を多用する人は記憶に留めておくといいかもしれない。

\bxtexlogosetup: パラメタ設定する件

key-value形式でパラメタ設定を行う命令が新設された。

\bxtexlogosetup{‹パラメタ›=‹値›,…}

現状で有効なパラメタを以降で紹介する。

ghost: ゴーストする件

TeX関連のロゴは基本的に欧文なので、和文の文書中で用いる場合は周りの和文文字との間に和欧文間空白が適切に入ることが望ましい。しかしロゴのような「凝った加工」を施した文字があると自動の和欧文間空白挿入が失敗することが多い。これへの回避策となるのがbxghostパッケージによる「ゴースト挿入」である。

bxtexlogoではロゴの周りに自動的にゴーストを入れる機能を用意して、ghostパラメタで有効化できる。

  • ghost=‹真偽値›(既定=偽): 自動ゴースト挿入を有効にするか。
    trueにする場合はbxghostが読み込まれている必要がある。

すなわち、以下の手順3で自動ゴースト挿入が有効になる。

  1. bxghostパッケージ4を読み込む。
  2. bxtexlogoのパラメタghosttrueに設定する。

ゴースト挿入の効果をみるための例を示す。

% upLaTeX+dvipdfmx文書
\documentclass[uplatex,dvipdfmx,a4paper]{jsarticle}
\usepackage{bxghost,bxtexlogo}% bxghostを読み込む
\bxtexlogoimport{SATySFi}
\begin{document}
% 比較のため, ghost無効の状態で出力する
お客様の中に{\SATySFi}芸人はおられますか。

% ghostを有効にして出力する
\bxtexlogosetup{ghost=true}% ghostする
お客様の中に{\SATySFi}芸人はおられますか。
\end{document}

※この文書ではゴースト挿入が有効と無効の状態を比較するために敢えて本文の途中でghosttrueにしているが、もちろん実際にはプリアンブルで設定することが普通であろう。

ghostを有効にするとSATySFiロゴの両側に正しく和欧文間空白が入っていることがわかる😊

one-font: 1を太くしたい話

「1TeX」のロゴの「1」の文字はいわゆる「黒板太字」で書かれる。「黒板太字」であればどの書体であっても「正しいロゴ」と見なされるわけだが、1TeXの作者(某ZR氏🙃)は「dsrom」という書体(doublestrokeパッケージ)を使っている。このため、bxtexlogoの既定の動作では「1」のフォントとしてdsromが優先的に5使っている。

dsromはカッコイイ書体(多分🙃)であるが「太字版がない」という欠点(ええっ😲6)がある。だから太字の書体の中で1TeXのロゴが現れる場面においては既定動作ではバランスが悪く見えるかもしれない。

太字版がある黒板太字のフォントというと「bboldx」というものが存在する。そこで、bxtexlogoではこのフォントに切り替えるためのパラメタを用意している。

  • one-font=‹値›: 「1TeX」ロゴの「1」の書体を指定する。
    • normal(既定): 既定の動作。最優先でdsromを使う。
    • bboldx: bboldxを使う。

実際に「1」の書体をbboldxに切り替える例を示す。

% pdfLaTeX文書
\documentclass[a4paper]{article}
\usepackage{graphicx,bxtexlogo}
\bxtexlogoimport{OneTeX}
\bxtexlogosetup{one-font=bboldx}
\begin{document}
I love {\OneTeX}! % 中字
\textbf{Yes, I love {\OneTeX}!!} % 太字
\end{document}

これで良くなったかは微妙だが、取りあえず太字のロゴとしての整合性は取れているだろう🙃

その他諸々の話

他のパラメタについては使用する機会が乏しいと思われるので詳細は省略する。

  • smallcaps=‹値›: スモールキャップの文字を出力する場合に、実際のフォントの指定を行うか疑似出力(小さいサイズの大文字を出す)を行うか。

    • auto(既定): 「CM Sans Serif」と「LM Sans Serif」のときだけ(スモールキャップがないことが判っているので)疑似にする。
    • real: 常に実際のフォントの指定を行う。書体にスモールキャップがない場合はフォールバックが起こるので不正確な出力がになりうる。
    • fake: 常に疑似にする。
  • hologo-for-basic=‹真偽値›(既定=偽): 本パッケージ提供のロゴの中の「TeX」「LaTeX」「LaTeX2e」の部分の出力にhologoの該当のロゴを使うか。偽の場合は現在定義されている\TeX\LaTeX\LaTeXe命令を使う。

まとめ

というわけで皆さん、新しいbxtexlogoパッケージでドンドン「1TeX」ロゴを書いていきましょう!💁(えっ😲)


  1. もっとも、hologo提供のロゴなら\hologo命令でも使えるので\bxtexlogoを使うメリットはないのであるが。
  2. ただしTeXのレベルで考えると、実は\bxtexlogoimport{1TeX}により1TeXという名前の制御綴は実際に定義される。
  3. 将来、一定の条件下でghostの既定値を有効にする改修を行うかもしれない。
  4. bxghostの代わりにbxghost-libを読み込んでもよい(知っている人向け情報)。
  5. 「dsrom→bbm→bbold」の順で最初に見つかったものを使い、どれもない場合は通常の太字の「CM Roman BoldExtended」にフォールバックする。
  6. 元々数式用の「黒板太字」なのだから太字版がないのはアタリマエである。

dvisvgmのフォントマップ指定がよくわからない件

dvisvgmの「文書中のspecial命令によるフォントマップ指定」の機能がよくわからないので、チョット調べてみた。

公式ドキュメントの説明では

公式ドキュメントdvisvgm.man1.pdfには次のように書かれている。

pdf:mapfile and pdf:mapline allow for modifying the font map tree while processing the DVI file. They are used by CTeX, for example. dvisvgm supports both, the dvips and dvipdfm font map format.

  • dvisvgmはdvipdfmxのpdf:maplinepdf:mapfileのspecial命令をサポートする。
    • dvipdfmxと同様に、dvipsとdvipdfmの両方のマップ行形式をサポートする。

実際の動作では

  • マップ行の修飾子(+-)はサポートしていない
    • 例えば「+foo …」というマップ行を書くと「+fooというTFMに対するマップ指定」と見なされる。
  • 既に行われたマップ指定を変更する操作はサポートしていない😢😢😢
    • 「修飾子無し」のマップ行はdvipdfmxでは「古い設定を置換」になるはずだが、実際には無視されてしまう。
  • dvipdfmx形式のマップ行の-lオプションはサポートしていない
    • pdf:mapline: invalid option: -l」という警告が出る。

和文フォントに対する動作は

dvisvgmはdvipdfmxの和文用のマップ行指定(第2項にCMapを指定する)に対応しているようだ。

dvipdfmxで有効な設定パターンを用いて出力がどうなるかを試してみると以下のようになった。

[表の見方]

  • TTF: 物理フォント(第3項)としてTrueTypeグリフのフォントを指定
  • CFF: 物理フォントとしてCFFグリフのフォントを指定
  • OL化: アウトライン化(--no-font)を指示
  • 埋込: データ埋込(--font-format=woff2)を指示
  • ❌化け: 正しくない文字が出力された
  • ❌ズレ: 文字の配置が異常になった
  • ⭕: フォントの既定のグリフで正常に出力された(字形区別は非反映)
  • 💯: CMapが定める字形に従って正常に出力された

[pTeX・JIS系の和文TFM使用時]

Cmap指定 TTF/OL化 TTF/埋込 CFF/OL化 CFF/埋込
H
2004-H

[upTeX・Unicode系の和文TFM使用時]

Cmap指定 TTF/OL化 TTF/埋込 CFF/OL化 CFF/埋込
UniJIS-UTF16-H ❌化け ❌化けズレ 💯2000JIS ❌ズレ
UniJIS2004-UTF16-H ❌化け ❌化けズレ 💯2004JIS ❌ズレ
unicode
  • HとCFF」の組み合わせで字形の区別が反映されないのが不可解である🤔
    • 一見、CMapを無視して独自で「JIS→Unicode」で変換しているように思えるが、実際はそうではなく、HのCMapファイルの内容は正しく解釈されているようだ1
    • dvipdfmxではTTFの場合(CIDアクセス不可)に「CMapでCIDに変換した後、ToUnicodeマップ(Adobe-Japan1-UCS2)でさらにUnicodeに変換する」という処理が行われるが、dvisvgmはこれをCIDアクセス可能なCFFの時にも適用しているように思える。
    • 元がUnicodeである場合はこれと正反対で、TTFとCFFのどちらの場合も「さらにUnicodeに変換」を適用していないと思われる。このため、CFFでは正しい字形が選ばれるが、TTFでは文字化けになってしまう。
  • UnicodeのTFMで埋込を指定すると文字の配置が異常になる🤔
  • CMapのunicode指定は安定して正常に機能する。
    • Adobe-Identity0のフォントでも大丈夫。

まとめ


  1. 「CMapファイルの内容をわざと改変する」等の方法で確認した。

LuaTeXで“relax化しないcsname”をもっとつくる件

前回の記事で「\relaxせずに\csnameする」クイズについて、Luaコードを利用して実装する正解例を示した。

ところで、例のクイズの補足説明を改めて見ると、少し気になる内容がある。

※もちろんLuaコードを使ってよい。
※ただしLuaコードを使わない回答ができればもっとよい🙃

実はこの問題はLuaコードを使わない(ただしLuaTeXの機能は利用する)解決法が存在する。本記事ではその方法を解説してみる。

必要な前提知識

  • フツーのTeX言語🤮の知識。

TeXの常識、LuaTeXの非常識

前回の問題の考察において、「“relax”化が起こった場合に代入操作で再度未定義の状態に戻す」という案を考えた。

\csnameによる“relax化”が避けられないなら、その後でもう一度その制御綴に未定義を代入すればよい。しかし今回の問題では完全展開可能で実装する必要があるため、代入操作は使えない。

完全展開可能の実装で代入は使えないというのは、TeX言語の常識である。……といいたいところだが、実はLuaTeXでは違う😲😲😲 LuaTeX拡張には「展開操作で代入を行うためのプリミティブ」が用意されている1のである。

  • \immediateassignment‹代入文›: [展開可能] 空トークン列に展開されるが、その副作用として‹代入文›で指示された代入が行われる。

ただしここで指示できる代入文には制限があるようである。例えばボックス代入は許されていない。

代入が可能であるのなら、以下のような素直な手順で要件が実現できるはずである。

  • まず\ifcsnameで当該制御綴が定義済かを判定する。
  • 判定が済んだ2ので普通に\csnameで制御綴を生成する。判定結果が真(定義済)だった場合はここで終了。
  • 判定結果が偽(未定義)だった場合、既に制御綴が手元にあるので、\immediateassignment付きの代入で制御綴を未定義に戻してから制御綴を置く。

とりあえず書いてみる

先の方針を素直にコードで表すと以下のようになる。

\def\myMakeCS#1{%
  % まず判定する
  \ifcsname#1\endcsname
    % 定義済の場合は簡単
    \csname#1\endcsname
  \else
    % 未定義の場合.
    % ここで`\csname`を1回展開して制御綴を作る(relax化発生)
    \expandafter\my@make@cs@undef\csname#1\endcsname
  \fi
}
\def\my@make@cs@undef#1{% #1は目的の制御綴
  % 制御綴を未定義に戻す
  \immediateassignment\let#1\my@undefined
  % 展開結果
  #1%
}

全体の動作だけみると、この実装で既に想定通りになっている。

% 完全展開可能性の確認のため \message 中で実行.
% \futurelet は展開不能なのでこれが最終結果になる.
\message{\myMakeCS{futurelet}}
%==>「\futurelet」と表示
% 未定義の制御綴の場合, (relax化せず)未定義エラーが出る.
*\message{\myMakeCS{duckduck}}
%==> \duckduck の箇所でエラー「! Undefined control sequence.」

しかしこの実装は要件を満たしていない。まず問題なのは「生成された制御綴よりも後方に\fiが残るせいで(完全展開可能ではあるが)先頭完全展開可能になっていない」ことである。仮に“後方の部分”を無視したとしても、「if文が真の場合と偽の場合で制御綴に到達するまでの展開回数が異なる」という問題が残っている。

これらの問題を解決するために「展開の加速」という技法を利用することにする。展開の加速については以前に別の記事で紹介した。

ここでは\expandedを利用して完全展開可能な(しかし先頭完全展開可能とは限らない)マクロを「2回展開」に加速する手法3を用いる。これによって(マクロが常に2回展開になるので)先述の2つの問題が同時に解決できてしまう。

では早速マクロを修正してみる。\expandedによる加速を適用するのは簡単で、マクロの定義本体を\expanded{…}で囲うだけでよい。

\def\myMakeCS#1{%
  \expanded{% 加速する
    \ifcsname#1\endcsname
      \csname#1\endcsname
    \else
      \expandafter\my@make@cs@undef\csname#1\endcsname
    \fi
  }%
}

ただ今の要件には少し厄介な点があり、それは「制御綴が現れたらそこで展開を止める必要がある」ことである。これは\unexpandedプリミティブ4を使えば解決できる。例えば\my@make@cs@undefマクロの最後の#1(=所望の制御綴)はそれ以上展開してほしくないので\unexpandedで囲えばよい。

\def\my@make@cs@undef#1{% #1は目的の制御綴
  \immediateassignment\let#1\my@undefined
  % これが展開結果なので \unexpanded で展開を止める
  \unexpanded{#1}%
}

\myMakeCS中の\csname#1\endcsnameは、それを1回展開してそこで止まってほしい。これは\unexpanded\expandafterと組み合わせる5ことで解決できる。

      \unexpanded\expandafter{\csname#1\endcsname}%

これで要件通り(n=2)に動作するプログラムが完成したことになる🙂

正解例(Luaしないやつ)

最終的なプログラムは以下のようになった。

%% \myMakeCS{‹文字トークン列›}: 2回展開すると`‹文字トークン列›`を
% 名前とする制御綴になる.
\def\myMakeCS#1{%
  \expanded{%
    \ifcsname#1\endcsname
      \unexpanded\expandafter{\csname#1\endcsname}%
    \else
      \expandafter\my@make@cs@undef\csname#1\endcsname
    \fi
  }%
}
\def\my@make@cs@undef#1{% #1は制御綴
  % 制御綴を未定義に戻す
  \immediateassignment\let#1\my@undefined
  \unexpanded{#1}%
}

前回と同じ例を試してみる。こちらも期待通りに動いているようだ😌

\let\XA\expandafter
\XA\XA\XA\show\myMakeCS{noexpand} % 定義済の制御綴
%==>「\noexpand=\noexpand.」と表示
\XA\XA\XA\show\myMakeCS{duck?duck!} % 未定義の制御綴
%==>「\duck?duck!=undefined.」と表示
\XA\XA\XA\show\myMakeCS{"アレ\string\?} %引数は「"アレ\?」
%==>「\"アレ\?=undefined.」と表示

おまけ(Luaしない別のやつ)

この投稿をしたときに用意していたプログラムは以下のものだった。

\def\myMakeCS#1{%
  \romannumeral-`>\ifcsname#1\endcsname
    \expandafter\my@make@cs@a\expandafter\space
  \else \expandafter\my@make@cs@a\expandafter\my@make@cs@b
  \fi{#1}}
\def\my@make@cs@a#1#2{%
  \expandafter#1\csname#2\endcsname}
\def\my@make@cs@b#1{%
  \immediateassignment\let#1\@undefined
  \space#1}

このプログラムでは\expandedの代わりに\romannumeralトリックによる先頭完全展開を利用している6

まとめ

というわけで、キャンペーン🌸🍀期間内でも期間外でもとにかく皆さん、ドンドンTeX言語🤮しましょう!💁


  1. なお「LuaコードでTeXパラメタへの代入を行う」ことでも実質的に「展開操作で代入」を行える。しかし今回の問題については「制御綴に“未定義”を代入する」をLuaコード上で行う方法が自分には発見できなかった😢
  2. 判定が済む前に\csnameを使ってしまうと元々未定義だったのか\relaxだったのかが区別できなくなり失敗する。
  3. 参考記事にあるように「Luaしない」前提であれば加速は「2回展開」までが限界である。前回の正解例は実質的にLuaで実装されているので、参考記事の技法を使えば「1回展開」に加速することが可能である。
  4. \expanded{…}で完全展開しているトークン列の中に\unexpanded{‹何か›}があった場合、その展開結果は‹何か›になりそれ以上展開されない
  5. \unexpandedはグループを引数にとるプリミティブなので、中を1回展開したい場合に\unexpandedの前に\expandafterを付ける必要はない。
  6. \romannumeralトリックによる先頭完全展開を途中で止めたい場合は、トークン列の先頭にわざと空白トークンを生じさせればよい。