今さら人に聞けないMETAFONT

  • 9
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

この記事は TeX & LaTeX Advent Calendar 2015 の18日目の記事です。
昨日は あざらし さんでした。明日は umireon さんです。

最近METAFONTの素晴らしさに気づきいろいろ勉強していたのですが,METAFONTのライトユーザー向けの日本語文献がほとんど存在しないことに気づき1,せっかくなので備忘録の意味も込めてまとめてみました。

※ 筆者自身の環境は TeXLive2015 (OSX El Capitan) です。従ってWindowsユーザー向けの情報は不十分・不正確な可能性があります。

起動方法

起動できないとお話にならないので,とりあえずは起動方法から。

TeXLiveがインストールされているのであれば,ターミナル(コマンドプロンプト)上でmfと入力すればOK。対話環境がスタートします。


$ mf
This is METAFONT, Version 2.7182818 (TeX Live 2015) (preloaded base=mf)
**

普通の場合であれば,最初の一言でまず\relaxと入力してから始めましょう2

$ mf
This is METAFONT, Version 2.7182818 (TeX Live 2015) (preloaded base=mf)
**\relax

*

あるいは,予めmyfont.mfなどMETAFONTファイルをテキストエディタで作成してから,

$ mf myfont.mf

として読み込ませたり,

$ mf
This is METAFONT, Version 2.7182818 (TeX Live 2015) (preloaded base=mf)
**\relax

*input myfont;

として対話モード中にロードすることも可能です。

エディタ

METAFONTをこの先バリバリ扱っていくわけですから,シンタックスハイライトぐらいには対応したエディタを使いたいものです。ところが残念な(意外な?)ことに,METAFONTは非常にマイナーな言語らしく,有名な(GUI)エディタはほとんどMETAFONTをサポートしていません。

というわけで「METAFONTを使いたい奴はEmacsかVimだ!」3と終わらせても良いのですが,いかんせん自分のようなライトユーザーにとってCUI環境は若干敷居が高いので4,METAFONTに対応しているGUIエディタをご紹介します。

Mac(,Unix)向けエディタ

CotEditor

公式ページはこちら。Mac界では言わずと知れた有名エディタですね。2015年中にはMETAFONTに対応する予定です(実は私の作成したMETAFONTシンタックススタイルが次リリースのバージョン2.3.2から組み込まれるのですが,公開の前にアドベントが回ってきてしまいました…)。

2015/12/19追記:バージョン2.3.3がリリースされ,METAFONTに対応しました。

Kate

公式ページはこちら。Linux界隈では有名なエディタらしいですが,Mac版も存在するようです。

Windows向けエディタ

Emerald Editor

公式ページはこちら(2015年12月18日現在,なぜかリンク切れ...)。昔から存在するCrimson Editorというエディタの後継らしく,将来的にはMacやLinuxにも対応する予定だそうです。

METAFONTの文法

さて肝心の文法ですが,こんなサンプルコードを追っていくことにしましょう。

dangerous_bend.mf

mode_setup;

h_height# := 7.5pt#;
u# := 20/36pt#; baselinedistance# := 11pt#;
define_pixels(u,baselinedistance);
rulethickness# := .5pt#; heavyline# := 50/36pt#;
define_blacker_pixels(rulethickness,heavyline);

beginchar(65,25*u#,h_height#,0); "Dangerous bend symbol";
  pickup pencircle scaled rulethickness; top y1 = 25/27h; lft x4 =0;
  x1 + x1 = x1a + x1b = x4b + x2a = x4 + x2 = x4a + x2b = x3b + x3a = x3 + x3 = w;
  x4a = x4b = x4 + u; x3b = x1a = x1 - 2u;
  y4 + y4 = y4a + y4b = y3b + y1a = y3 + y1 = y3a + y1b = y2b + y2a = y2 + y2 = 0;
  y1a = y1b = y1 - 2/27h; y4b = y2a = y4 + 4/27h;
  draw z1a .. z1 .. z1b --- z2a .. z2 .. z2b --- z3a .. z3 .. z3b --- z4a .. z4 .. z4b --- cycle; %標識のボード
  x10 = x11 = x12 = x13 = .5w - u; x14 = x15 = x16 = x17 = w - x10;
  y10 = y14 = 28/27h; bot y13 = -baselinedistance;
  z11 = (z10 .. z13) intersectionpoint (z1a{z1a - z4b} .. z1{(1,0)});
  y15 = y11; y16 = y12 = -y11; y17 = y20 = y21 = y13;
  draw z11 -- z10 -- z14 -- z15; draw z12 -- z13; draw z16 -- z17; %道標
  x20 = w -x21; x21 - x20 = 16u; draw z20 -- z21; %土台
  x36 = w -x31; x36 - x31 = 8u; x32 = x33 = x36; x31 = x34 = x35;
  y31 = -y36 = 12/27h; y32 = -y35 = 9/27h; y33 = -y34 = 3/27h;
  pickup pencircle scaled heavyline;
  draw z32{z32-z31} .. z33 --- z34 .. z35{z36 - z35}; %急カーブの印
  pickup penrazor xscaled heavyline rotated (angle(z32 - z31) + 90);
  draw z31 --z32; draw z35 -- z36; %上側/下側の横棒
  labels(1a,1b,2a,2b,3a,3b,4a,4b,range 1 thru 36);
endchar;
shipit; end.

…言わずと知れた「あの文字」(下図)を表すコードです5

dangerousbend.png

大雑把に言って,METAFONTのソースコードはプリアンブル(的なもの)と文字の描画部の2要素で構成されます。begincharからendcharまでが文字の描画部で,その前にちょろっとあるのがプリアンブルです。

あと,コードの見かけから何となく分かりますが,METAFONTの各種コマンドは末尾にセミコロン;が必要です。TikZと同じノリですね。それから,TeX同様%以下はコメントとみなされます。

ちなみに,この「文字」を実際にTeX文書内で使用する方法は,こちらの記事に詳しく記載されています(texadventでもMETAFONTネタは何だかんだ毎年1回は出てくるみたいですね)。
なお,リンク先の記事で登場するmktextfmは内部でmfコマンドを実行しているので,ユーザーがわざわざMETAFONTを起動する必要はありません。

プリアンブル(的なもの)について

プリアンブルでは,フォントファミリー全体で共通のパラメーターを用意したり,出力装置に合わせた解像度の設定を行ったりします。

適切な解像度の設定

冒頭のmode_setupは,出力装置に合わせた解像度の設定を行なってくれるコマンドです。METAFONTには\modeという特殊な引数が存在し,適切な設定を施すと同じソースから低解像度用・高解像度用など複数のフォントを生成できたりします(これを入れなくともなぜか処理は通りますが,モードによる場合分けが効かなくなるようです)。

例えば,

$ mf \mode=cheapo; input myfont;

としたら低解像度用のフォントが生成されて,

$ mf \mode=luxo; input myfont;

としたら高解像度用のフォントがでてくる…なんて事が可能です6

変数の設定

ソースから分かる通り,METAFONTにおける変数への値の代入(と同時に変数の宣言)は:=で行います。読めば分かると思いますが,例えばbaselinedistance#は11pt,すなわち1/72.27インチの11倍の大きさを格納しています。これらの末尾に存在する#「本当の」値を意味しており,\modeによらず絶対に11ptそのものを表し続けます。

define_pixelsdefine_blacker_pixelsは,mode_setupとも関連しますが「本当の」値を\modeに応じた値(ピクセル数)に変換するコマンドで,同じ11ptのbaselinedistance#であってもひとたびdefine_pixelsを通せば,高解像度用の出力と低解像度用の出力で値が変わることになります7

例えば1ptをピクセル(の1辺の長さ)3個分で表現する低解像度の印刷機向けモード cheapoで処理するとbaselinedistanceの値は33となり,ピクセル30個で表現する高解像度モード luxo で処理すると330となるわけです。

define_blacker_pixelsは,define_pixelsよりもほんの僅か大きめに値を変換するコマンドです。例えばrulethicknessは0.5ptなので,cheapoモードであればそれは1.5という値に変換されるべきなのですが,define_blacker_pixelsを通すとその値にさらに0.65上積みされた,2.15という値に変換されます。

このコマンドで処理している変数の名 (rulethickness,heavyline)から想像出来る通り,define_blacker_pixelsは「線の幅」の変換専用にチューニングされたdefine_pixelsだと思ってくれて差し支えありません。なぜdefine_pixels本来の変換結果よりも僅かに値を大きめに取るのかと言うと,「インクの滲み」を考慮に入れるためだからだそうです。印刷機あるいは用紙などによって,インクの滲み具合は当然異なるわけですから,それぞれに対応するモードで「値の上乗せ程度」を別個に設定すればどこでも同様な出力が得られるという訳です。Knuthのこだわりが垣間見えますね…。

なお,特にモードを指定せず処理した場合も,自動的にproofモードで処理されるため,エラーが出ることはありません。

文字の描画部について

begincharの後に続く引数は,それぞれ順に「文字コード」「幅」「高さ」「深さ」を表します。「文字コード」にはASCII文字コード(10進数)を指定するか,"0"のように相当する文字を引用符で囲んで入れれば良いです。サンプルでは65すなわち小文字のaに対応する字形として,dengerous bendを作成しています。

描画の大まかな流れとしては,

  1. 各点の座標を設定
  2. pickupで線の種類を指定
  3. drawで各点間を結ぶ

となります。この「各点の座標を設定」する方法がMETAFONT特有で,なんと連立方程式を自動で解いてくれるのです。普段からTeXでプログラミングをしていると,これが物凄く便利に感じられますね!

なお,METAFONTでは各点の位置ベクトルを z1, z2, z3, ... と表し,それらの位置ベクトルのxy座標をそれぞれ x1, x2, x3, ..., y1, y2, y3, ... と表現します8

具体的には例えばサンプルのこの部分,

  lft x4 =0;
  x1 + x1 = x1a + x1b = x4b + x2a = x4 + x2 = x4a + x2b = x3b + x3a = x3 + x3 = w;
  x4a = x4b = x4 + u; x3b = x1a = x1 - 2u;

単に連立方程式を列挙したけですが,これだけで点 z1, z1a, z1b, z2,..., z4 までの x 座標が指定されたことになります(lftはその点を通る線の左端を,指定した座標に揃えるコマンド)。

また,draw..で連結した点をいい感じにベジェ曲線で結び,--は直線で結びます。---は「無限の張りをもつ結合」で,両端の先の曲線と滑らかに繋がるような,直線に近い曲線となります。

結局のところ,各点はdengerous bendの以下の部分に対応しています。
mfput.jpg
線の細い長方形は,「TeXで文字の大きさとみなされる部分」を表します。従って,TeX文書内でこの文字を使用した場合,標識の下半分がベースラインからはみ出ることになります9。それにより,文字の上下幅がちょうど2文字分となり,以下のようにきっちり組版することが出来るわけです。

The_TeXbook.png

The TeXbook (Donald E. Knuth) の組版例

まとめ

某Adobeさんが開発中のProject Facesのパクリと同じサービス,METAFONT / MetaPostで作れないかなぁ...


  1. 人に聞けない(物理) 

  2. METAFONTの作者もTeX同様Knuthなので,類似したコマンド名も多く存在します。 

  3. EmacsとVimにはいずれもMETAFONT用の編集モードが備わっています。  

  4. 前節でターミナルを持ち出た癖にアレですが,私自身日常的に使っているエディタはTeXShopくらいなので,EmacsにもVimにも全然慣れていません。 

  5. 『METAFONTブック』に登場するソースコードの抜粋を,単独で動くように私が手を加えたものです。 

  6. cheapoluxoはMETAFONTで既に用意されているモードですが,一般には予めmode_defなどを使い,(TeXのマクロのように)「このモードでは何をどうすべきなのか」を指定しておく必要があります。 

  7. 正確には,cheapoでは1ptを2.7674ピクセルで表現し,luxoは27.674ピクセルで表現するように設定されています。 

  8. 正確には,サンプルにも登場してるz1aのように, [x, y, z] + (英数字) は点 (英数字) とみなされます。 

  9. コード内でbaselinedistanceが11pt(に相当するピクセル数)に設定されているため,点z13, z17, z20, z21が11ptぶん下に潜ります。そもそもこの文字は,本文中の文字を11ptで組版する際に使用することを想定されています。