マクロツイーター

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

jfmutilで和文VFをカスタマイズする話(1)

どうやらアヒルらしいので、雑に何か書いてみようと思う。

※ただし、例の「黄色い本」の10章に書いてあるようなVFのキホン的な事項については余裕で解っていることを前提とする(うわぁ)

事例1:倍角ダーシをつなげたい話

同人誌で小説を書く人の間では有名なように、世の中にはダーシを2つ並べた場合につながってみえるフォントと隙間ができるフォントがある。TeX Live(のDVIウェアのフォントマップ設定)において既定の明朝体フォントとなっている「IPAex明朝」はダーシがつながらない。

ダーシがつながらない例
ダーシがつながらない例

そして、世の中には「ダーシがつながる」ことに非常に拘る人がいる。それだけなら「ダーシがつながるフォントを選択すればよい」だけであるが、そういう拘りをもつ人は大抵フォント自体にも拘る。なので「〇〇のフォントが大好きだからぜひ使いたいのだけど、〇〇のダーシがつながらないのだけは困る」という悩みが出てくる。

VFを活用すると、「フォント〇〇を使いつつ、全角ダーシ(U+2015)だけは“つながるダーシ”をもつ別のフォント△△を使う」というような設定が可能になる。

要件を整理してみる件

  • upTeXの標準の横組和文フォントupjisr-hをカスタマイズして新しいフォントmy1upjisr-hを作製する。
  • my1upjisr-hでは全角ダーシ(U+2015)の字形が「梅明朝1」(ume-tmo3.ttf)のものになる。
    ※「梅明朝」は“つながるダーシ”の字形をもつ。
  • my1upjisr-hのその他の性質は全て元のupjisr-hに合わせる。
    • 従って、全角ダーシ以外の文字のフォントは“通常の”設定に従う。(TeX Liveの既定の設定では「IPAex明朝」である。)

基本的な設計を考える件

upjisr-hの参照関係は以下のようになっている2

upjisr-h (VF)
   0→uprml-h (原TFM)
   2→upjisr-hq (VF)
         0→uprml-hq (原TFM)

ここで現れる2つの原TFM3は「upTeXの標準の原TFM」であり、TeX Liveの和文フォント設定ツール(kanji-config-updmapなど)の設定対象となっている。初期設定では以下のようになっていて4、「IPAex明朝」が割り当てられている。

uprml-h   UniJIS-UTF16-H  ipaexm.ttf %!PS IPAexMincho
uprml-hq  UniJIS-UCS2-H   ipaexm.ttf %!PS IPAexMincho

さて、これから作製するmy1upjisr-hでは、「梅明朝」への参照を追加する必要がある。従って、新たな原TFMである「my1uprml-hd」5を用意して、それをフォントIDの1番に追加することにしよう6

my1upjisr-h (VF)
   0→uprml-h (原TFM)
   1→my1uprml-h (原TFM) ※追加
   2→upjisr-hq (VF)
         0→uprml-hq (原TFM)

これ以上の設計の詳細については、作業しながら考えていくことにしよう。

作業を開始する件

まずはupjisr-hのVFをZVP形式のテキストに変換する。

$ jfmutil upjisr-h

upjisr-h.zvpが出力されるが、これから行うのは「my1upjisr-hというVFを作る」ことであるので、ファイル名をmy1upjisr-h.zvpに変えておこう。

参照TFMを追加する件

ZVP形式においては、VPL形式と同様のFONTMAP要素によって参照TFMを定義を行う。

[my1upjisr-h.zvp:18行目]

(MAPFONT D 0
   (FONTNAME uprml-h)
   (FONTCHECKSUM O 0)
   (FONTAT R 1.0)
   (FONTDSIZE R 10.0)
   )
(MAPFONT D 2
   (FONTNAME upjisr-hq)
   (FONTCHECKSUM O 0)
   (FONTAT R 1.0)
   (FONTDSIZE R 10.0)
   )

ID=0にuprml-h、ID=2にupjisr-hqが定義されている。ID=1にmy1uprml-hdを追加することにしよう。

(MAPFONT D 1               ;ID
   (FONTNAME my1uprml-hd)  ;TFM名
   (FONTCHECKSUM O 0)      ;チェックサム値を使わない
   ; パラメタ……

参照TFMのパラメタはどうすればいいだろうか。元のupjisr-hのグリフはダーシも含めて大部分がuprml-hを用いて出力されている。ここで追加したいmy1uprml-hdとuprml-hを比べると物理フォントが異なる(これはVFの外で指定する事項)他は同じ性質をもつ前提である。なので、uprml-hと同じにすればよい。

(MAPFONT D 1
   (FONTNAME my1uprml-hd)
   (FONTCHECKSUM O 0)
   (FONTCHECKSUM O 0)
   (FONTAT R 1.0)          ;スケール
   (FONTDSIZE R 10.0)      ;my1uprml-hdのデザインサイズ
   )

これをmy1uprml-h.zvpに追記するわけであるが、その場所については一つだけ注意がある。ZVP中で最初に書かれたMAPFONTには特別な意味がある。なので、特にその意図がない限りは「先頭のMAPFONTがどれか」を変えてはいけない。その他については制約はなく7、特にIDの番号順に並んでいる必要はない。

ダーシの定義を与える件

VPL形式では(仮想の)グリフの定義はCHARACTER要素で行うのであった。ZVP形式のmy1upjisr-h.zvpを見ると、CHARACTER要素とJPL形式のTYPE要素が複合したような形の「中にMAP要素を含むTYPE要素」がある。

[my1upjisr-h.zvp:173行目]

(TYPE D 0
   (CHARWD R 1.0)
   (CHARHT R 0.88)
   (CHARDP R 0.12)
   (MAP
      (SETCHAR)
      )
   )

このようにZVP形式においてはグリフの定義は「TYPE要素」「SUBTYPE要素」「CHARACER要素」の組み合わせで表す。しかし、今回のように「メトリックの変更を伴わない」カスタマイズの場合は、取りあえずは「変更したい文字のCHARACTER要素を追記する8」という方法だけを知っていれば対処できる。今回はU+2015の定義を修正したいので、次のようなものを書くことになる。

(CHARACTER H 2015      ;U+2015の文字の定義
   (MAP
      ;仮想グリフを出力するDVI命令…
      )
   )

ここで、U+2015が元々どのように定義されていたかを確認してみる。CHARSINTYPEの定義から判断すると、U+2015は文字クラス5に該当するので、TYPEの5の定義を見る。

[my1upjisr-h.zvp:222行目]

(TYPE D 5
   (CHARWD R 1.0)
   (CHARHT R 0.88)
   (CHARDP R 0.12)
   (MAP
      (SETCHAR)
      )
   )

「引数のないSETCHAR」という妙な要素があるが、これはZVP形式独自の拡張で、「入力の文字と同じ文字コードのグリフをsetcharする」ことを表す9。つまり、U+2015に対する定義としては、この(SETCHAR)(SETCHAR H 2015)と等価になる。要するに、元の文字を何も加工せずにそのまま出しているだけである。

※そのまま出力するといってもフォントはどれになるのかが気になるだろう。実は、SELECTFONTを実行する前の「既定のフォント」は「ZVP中で最初に出現したMAPFONT」である。つまりU+2015の定義は「uprml-hのU+2015を出力する」となる。先に「先頭のMAPFONTは特別」と書いたのはこういう意味である。

今回のカスタマイズでは、U+2015のフォントをmy1uprml-hdに変えたいのであった。従って、SETCHARの前にSELECTFONT命令を入れればよい。

(CHARACTER H 2015      ;U+2015の文字の定義
   (MAP
      (SELECTFONT D 1) ;フォントをID=1の参照TFMに変える
      (SETCHAR)        ;U+2015を出力
      )
   )

VFが完成した件

以上でZVPの改修の作業は完了である。結局、以下のコードを追加することになった。

(MAPFONT D 1
   (FONTNAME my1uprml-hd)
   (FONTCHECKSUM O 0)
   (FONTAT R 1.0)
   (FONTDSIZE R 10.0)
   )
(CHARACTER H 2015
   (MAP
      (SELECTFONT D 1)
      (SETCHAR)
      )
   )

これらをmy1upjisr-h.zvpに追記した上で、VFに変換する。

> jfmutil my1upjisr-h

無事にmy1upjisr-h.vfとmy1upjisr-h.tfmが出力されれば、VFは完成である。

(つづけ)


  1. 「梅明朝」を選択した理由は単に“つながるダーシ”をもつフォントの一つであるからであり、それ以上の理由はない。物理フォントの選択はフォントマップの部分に切り出されるので後で他の物理フォントに変更するのも容易である。

  2. こういう情報は事前に知識として知っておく必要がある。知らないのであれば、取りあえずZVP形式に変換してその結果を読んで調査することになるだろう。

  3. 「VFではないTFM」のことをこの記事では「原TFM」を呼ぶことにする。jfmutilの説明書では「原メトリックTFM」、makejvfでは「PSフォントTFM」と呼ばれている。原TFMはフォントマップによって物理フォントに割り当てられる。

  4. これは「updmap用のフォントマップ」である。updmap用の和文のフォントマップは基本的にはdvipdfmx用のフォントマップの書式に準じるが、%!PSの後にdvips用の情報を補足するという規則になっている。

  5. TFMの名前は何でもよい。標準のTFMの「uprml-hq」の「q」は「引用符(quotation)用」という意図であろうから、それにならって「ダーシ(dash)用」ということで「-hd」という語尾にした。

  6. フォントIDの番号については、0~255の範囲で空いている番号なら何でもよい。なお、TFM名の語尾の「-hd」は「ダーシ(dash)用」という

  7. VPL形式については、確か「FONTMAPは全てCHARACTERの前に書く必要がある」などの制約があったはずである。

  8. 同じ文字のCHARACTER要素が複数回現れてはいけないので、既に当該の文字のCHARACTER要素がある場合はそれを修正する。

  9. 一般にVFのほとんどの文字は「入力と同じ文字コードのグリフをsetcharする」だけという単純なMAP定義をもつ。引数なしのSETCHARを導入することで、これらが全て「全く同一のMAP定義」と見なすことができて同じサブタイプにまとめることができるので、ZVPの記述を大幅に簡潔化できるわけである。