↔️

METAFONT 書体を改造して文字間隔を調節する

に公開

METAFONT 書体を改造して文字間隔を調節する

字間設定を変えたい

TeX(テフ)でよく使われるラテン文字書体は METAFONT(メタフォント)で美しく設計されていますが、英語の伝統的な綴りのみを想定したものです。
そのため、それ以外の綴りでは字間を調節したくなる場合もあります。

例えば、次のような組み合わせは、少し変えたい場合もあります。

  • 小文字の後に大文字が来る綴り:aT, aY, oT, oY, eT...
  • エスペラント語(世界語)のj: aj, oj


画像:cmr10 の例。aT, aY, oT, oY や aj, oj の間がやや広すぎるかも知れない。

小文字の後の大文字は、駱駝綴(キャメルケース, CamelCase)と呼ばれる、英語などのやや特殊な綴り方で出てくる組み合わせですね。

METAFONT 書体の原文(ソース)を改める

数カ所ですむなら TeX で \kern(詰めや字間調節)しても良いですが、全てを変更したいなら書体の設定を変えるのが良いです。
字間設定は、.mf から作られる .tfm 書体寸法指標(フォントメトリクス)によって TeX に参照されます。

今回は roman.mf, cmr10.mf を変更します。
まず .mf 文件(ファイル)を取得します
https://ctan.org/tex-archive/fonts/cm/mfhttps://mirrors.ctan.org/fonts/cm/mf.zip から受信(ダウンロード)できます。

次に、cmr10.mfroman.mf を .texと同じ場所に移動し、cmr10A.mfromanA.mf などに改名します。
改変するときは文件名を変える決まりになっているので、改名してください(% IT MUST NOT BE MODIFIED IN ANY WAY UNLESS THE FILE NAME IS CHANGED!とある)。

そして、cmr10A.mfgenerate roman の行を、romanA.mf を読み込むように generate romanAに変更します。

エスペラント語のjを詰める例

romanA.mf
%...(前略)
 ligtable "o": "b": "p": "e" kern -k#, "o" kern -k#, "x" kern k#,
   "d" kern -k#, "c" kern -k#, "q" kern -k#,
  "a": if serifs: "v" kern k#, "j" kern u#, else: "r" kern k#, fi
  "t": "y" kern k#,
  "u": "w" kern k#;
%...(後略)

"a": if serifs: "v" kern k#, "j" kern u#, else: "r" kern k#, fiの行から"j" kern u#,を消して

  "a": if serifs: "v" kern k#, else: "r" kern k#, fi

に変更します。
これにより、oj, bj, pj, aj が変更されます。
もともと、少しu#の分を開ける設定だったようです。
でも少し開きすぎているように見えたので、消して詰めると良い具合になりました。


画像:bonaj, matenoj を適用前(cmr10.mf)、変更後(cmr10A.mf)の順で比較

クヌース先生がわざわざ開けていたのを考えると、開け詰めの具合は好みの問題かもしれません。
とはいえ、他の書体を見てもこれくらいは詰めていることが多い気もしますので、変えて良かったのだと思います。

aT などを詰める例

aT, aY などを詰めて調節します。

romanA.mf
%...(前略)
 ligtable "o": "b": "p": "e" kern -k#, "o" kern -k#, "x" kern k#,
   "d" kern -k#, "c" kern -k#, "q" kern -k#,
   "a": 
     if serifs: "v" kern k#, else: "r" kern kkk#, fi 
  "t": "y" kern k#,
  "u": "w" kern k#, % 変更 行末;->,
    "T" kern kkk#, "Y" kern kkk#; %追加
%...(後略)

これで、oT, bT, pT, aT, tT, uT, oY, bY, pY, aY, tY, uY に変更が適用されます。
#kなどは、前の部分で k#:=-.5u#; kk#:=-1.5u#; kkk#:=-2u#;と定義されていて、少し詰めると言う意味です。


画像:aTa, uTu, aYa を適用前(cmr10.mf)、変更後(cmr10A.mf)の順で比較

aTa, uTu, aYa の結果を見ると、変更後の方は、およそ対称的に配置されて良い具合になりました。

ただし、tT, tY のような組は今の設定変更によって詰めすぎてしまったようです。
また、eT, eYのような組み合わせはまだ変更できていません。


画像:tY, eY を適用前(cmr10.mf)、変更後(cmr10A.mf)の順で比較。eY は変更無しのため同一の結果となっている。

好みや用途に応じて更に設定しましょう。
この記事は基本的な変え方の紹介のみに留まります。

ligtabel

ligtabel 文法の簡単な解説については、「METAFONT ブック」395頁 (Appendix F) より引用します。

ligtable "f": "f" =: oct"013", "i" |=: oct"020", skipto 1;
ligtable "o": "b": "p": "e" kern .5u#, "o" kern .5u#, "x" kern-.5u#,
          1:: "!" kern u#;

この⼀連の指⽰を意訳すると次のようになる。
拝啓 TeX 殿。(略:1行目の説明)また、貴殿が ‘o’ や ‘b’ や ‘p’ を組む場合、この後に入⼒されている⽂字が ‘e’ か ‘o’ からだった場合には、その⽂字との間に空⽩の半分の⼤きさを加えていただきたい。逆に ‘x’ であれば、半分だけ差し引いていただきたい。さらに、感嘆符だった場合には、まるごと 1 単位を加えていただきたい。 なお、 ‘f’ の次に感嘆符が続く場合にも、最後と同じ処理を施していただきたい(理由はラベル ‘1::’ が付いているから)。
草々

組版実行

texファイルで\font\cmrtenA=cmr10Aとすれば使えます。
(もちろん LaTeX でも使えるはずです。ConTeXt で METAFONT を使う方法が分からないので誰か教えて下さい。)

a.tex
\font\cmrten=cmr10
\font\cmrtenA=cmr10A

\cmrten aTa \cmrtenA aTa
\cmrten uTu \cmrtenA uTu
\cmrten aYa \cmrtenA aYa

\cmrten tYt \cmrtenA tYt
\cmrten eYe \cmrtenA eYe

\cmrten bonaj \par
\cmrtenA bonaj \par

\cmrten matenoj\par
\cmrtenA matenoj\par
\bye

なんと、TeXLive などの現代の TeX 環境の TeX(tex, xetex, luatex, ptex, latexなど) には kpathsea というものが組み込まれ、必要なmfを自動で実行してくれるようです。
そのため、本来は METAFONT や gftopk が生成するcmr10A.tfm(書体指標)とcmr10A.600pk(圧縮済み字形)も、texを実行するだけで自動で生成されます。

その代わり、.*pk や .tfm を消さないと、.mf を更新しても古い書体が使われ続けるので、.mf 更新時は消すようにしましょう。
例えば、このような Makefile で make a.pdf などとします。

Makefile
%.pdf: %.tex *.mf
	rm -f *.tfm *.*pk
	tex $<
	dvipdfmx $(basename $<).dvi

点描書体と線描書体

書体(フォント)には、点描書体(ビットマップフォント)と線描書体(ベクトルフォント)があります。
点描書体は、画面上で拡大すると粗くギザギザに見えます。
線描書体は、曲線で定義されているためどれだけ拡大しても滑らかです。

METAFONT が生成する字形は、TeX 文書の要求にその都度応えて作られる点描の字形です。
そのため、今回の改変した書体も、画面上で拡大表示するとギザギザに見えます。

線描の cmr10

昨今の一般的な TeX 環境では、たとえ\font\cmrten=cmr10を指定しても、amsfonts と言う道具箱(パッケージ)に含まれる線描化された cmr10 書体 (.pfb) が優先的に自動で選択されるので、拡大しても滑らかです。
一方で、amsfonts が含まれないような、基盤のみ (infraonly) の TeX 環境では、本来の点描書体で組版されます。

何も指定していないのに cmr10.pfb が選択され、『METAFONT ブック』や『TeX ブック』の知識と異なる結果になるのは、ちょっとびっくりしてしまいますね。
amsfonts は cmr10.pfb の名前を変えるなどしたほうが良いのではないかとも思います。

線描化しない本来の METAFONT 組版の良さ

点描書体は、拡大すると粗くギザギザになってしまいますが、TeX 組版においては全く劣(おと)ったものではありません。
そもそも TeX 組版は紙への印刷を想定したものなので、粗く見えるほど拡大するのは間違った閲覧方法なのです。

点描書体で組まれていることは、TeX, METAFONT 本来の仕組みのみで組まれていることの現れでもあります。
線描化のような必要以上の処理を挟まないことで、TeX でそのまま組版できる書体を直に操れるという自由を再び得たとも言えるでしょう。
このことから、METAFONT による点描書体をあえて好んで選ぶというのも、一つの良い考え方と言えます。
そう分かると、点描書体で組まれた見た目には安心感も感じますね。

METAFONT 書体を含んだ PDF の仕様

METAFONT 書体を含んだ文書を TeX 組版(tex, dvipdfmx など)すると、PDF 内では第3型(Type 3)書体として含まれます。
書体の埋め込み状況は、Linux, Mac, Windows 対応の poppler に付属する殻命令(シェルコマンド)pdffontsemb(embedded=埋め込み)欄で確認できます。

第3型書体を避けて提出する場合

印刷所や学会など、提出先によっては、第3型書体を避けるよう求められる場合があるようです。
そのような場合、FireFox などで開いてその印刷機能でPDFとして出力すると第3型書体なしの PDF が得られるので、この変換で対応できます。
あまり意味のない変換ですが、形の上で要件を満たせます。
詳しくは今後、別の記事で解説するかも知れません。

最小動作環境

shell-nix の場合の最小動作環境の設定を示します。

shell.nix
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
  buildInputs = with pkgs; [
    (texlive.combine {
      inherit (texlive) scheme-infraonly
      tex dvipdfmx metafont mfware ;
})];}

TeXLive の「基盤のみ」(infraonly)枠組で sudo tlmgr install tex dvipdfmx metafont mfware した状態と同じです。

mfware に gftopk が含まれています。

再現性・保存性を極めたい方は、一行目を{ pkgs ? import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/nixos-25.05.tar.gz") {} }:に変更してください。
TeXLive が2024年版に固定されます。

その他

.tfm だけを変更する方法

今回は書体寸法指標(フォントメトリクス).tfm の生成もととなる .mf を変更して直しました。
他の方法として、.tfm を文字列編集可能な対応形式 .pl に変換して変更する方法もあるようです。

課題

駱駝綴り(キャメルケース)に完全対応した「計算機現代」(Computer Modern) 書体が欲しいと言う人が、幾らかいるかも知れません。
私はエスペラント語の調整だけで満足してしまったので、今後作ることもなさそうです。

力に余りある方は、駱駝綴対応版を完成させてみてください。
重複製作を避けるため、着手する時にこの記事への発言欄で手を挙げると良いと思います。

Zennfes 2025 infra

たまたま Zennfes 2025 が開催されていたので、テーマ:インフラ・セキュリティに参加させていただきました。
METAFONT による書体造りは、TeX 組版における基盤(インフラ)となる部分ですね。

造字機能付き組版系統の魅力

書体という組版の基盤(インフラ)部分から自在に操れるのは METAFONT の大きな魅力ですね。
私としては、METAFONT による造字機能は TeX 組版の魅力の半分程度を占めていると思っています。

おわりに

お読みくださりありがとうございました!
ハートやバッジを頂けますととても嬉しく思います。
ご感想、ご指摘などもお気軽にお寄せください。

METAFONT 書体でエスペラント語の文章を組むには(追記)

この記事をきっかけに、ZR さんが、METAFONT 書体でエスペラント語の文章を組む場合の問題点を説明してくださいました。
https://x.com/zr_tex8r/status/1979958276378017819

実際にエスペラント語文章を組むには分綴(ぶんてつ、ハイフンを付けた行分割)を考慮する必要があります。
TeX 規定の7ビット符号化方式は OT1 符号化と呼ばれています。
OT1 符号化: https://en.wikipedia.org/wiki/OT1_encoding
この符号化方式では、ĉ, ŭ などのアクセント付き文字の扱いが問題となり、そのままではうまく自動分綴できません。

参考:ユニコードの LaTeX の場合

ユニコード LaTeX (XeLaTeX, LuaLaTeX)でのエスペラント語の組版について ZR さんが詳しく記事を書いてくださいました。
https://zrbabbler.hatenablog.com/entry/2025/10/20/211142

ただし、この方法では書体への制御を失ってしまいます。
METAFONT・TeX 本来の思想にある「良い考え方」的な解決策も必要です。
https://x.com/zr_tex8r/status/1979964964741021864

案1:合成文字のままなんとかする?

もっとも TeX らしい解決策はアクセントの合成と分綴を両立させる方法かも知れませんが、これはどうやらできないのでしょうか。
まだ調べが足りない部分もあるので、plainTeX でのフランス語やドイツ語の組版事情もまた調べて見ようと思います。

案2:エスペラント語用の書体を作る

まず考えられるのは、エスペラント語用の .mf (cmr10eo.mfromaneo.mf)を作る方法です。
OT1 符号中のギリシャ文字など、エスペラント語には要らない文字が割り当てられている部分をエスペラント語用の文字に入れ替えます。

この方法は ZR さんも注で示唆しています。
ZR「「新たにエンコーディングを定義すること」も可能ですが、その場合その新たなエンコーディングに基づく(TeXの意味での)フォント一式も用意する必要があり、膨大な手間がかかります。」(https://zrbabbler.hatenablog.com/entry/2025/10/20/211142)

ちょっと手間ですが、文字数も多くなく、一度作るだけで良いので、割といい方法だと思います。
そして、TeX らしい一つの解決策だと思います。

案3:分綴は前処理する

ちょっと外れた方法ですが、別の言語で TeX 原文を前処理して全ての単語の全ての分綴位置に手動分綴命令\-を入れておく手があります。
分綴箇所の検出も TeX が受け持つという TeX の思想からはそれてしまいますが、私好みのやり方です。しかも手っ取り早くできそうですね。

機能を分離するこの方法は「一つのことは一つの器械にやらせる」という UNIX 思想に基づいています。
そもそも TeX は多くの機能が統合されすぎてうまく扱えません。

案4:理想的で根本的な解決

理想的で根本的な解決法は、文字数制限をなくしてユニコードに対応した METAFONTu(仮称、メタフォンチュ)とそれに対応した LuaTeX-MFu(仮称)を作る方法です。
これは、最も理想的ですが、めちゃくちゃ大変です。実現しないでしょう。
でも METAFONT に文字数制限があるのがそもそもの厄介な問題です。

謝辞

ZR さんのポストと記事に謝辞を申し上げます。

Discussion