Hatena::ブログ(Diary)

マクロツイーター このページをアンテナに追加 RSSフィード Twitter

2011-09-30

dvipdfmx で OpenType する件について (5)

前回の続き)

LaTeX サポートファイルの作成

最後にフォント定義ファイルを用意する。今の例(直立と斜体がある)は「持ち込む練習」で用いた「CM Sans Serif Quotation」と同じパターンになっているので、それに倣う(つまりイタリックを斜体で代替する)ことにする。

[t1mplus1p.fd]
\DeclareFontFamily{T1}{mplus1p}{}
\DeclareFontShape{T1}{mplus1p}{el}{n}{<->mplus1p-a-t1}{}
\DeclareFontShape{T1}{mplus1p}{el}{sl}{<->mplus1p-ao-t1}{}
\DeclareFontShape{T1}{mplus1p}{el}{it}{<->ssub*mplus1p/el/sl}{}
\DeclareFontShape{T1}{mplus1p}{l}{n}{<->mplus1p-l-t1}{}
\DeclareFontShape{T1}{mplus1p}{l}{sl}{<->mplus1p-lo-t1}{}
\DeclareFontShape{T1}{mplus1p}{l}{it}{<->ssub*mplus1p/l/sl}{}
%(以下他のウェイトについて同様の記述)

全く同様にして ts1mplus1p.fd も作成する(T1/t1 を TS1/ts1 に変えるだけ)。

ファイルの配置

前に LY1 エンコーディング用のファイルを配置した場所に追加することにする。OpenFont フォントファイル自身の配置は既に済んでいるはずである。

  • *.tfm → $LOCAL/fonts/tfm/public/mplus1p/
  • *.vf → $LOCAL/fonts/vf/public/mplus1p/
  • *.enc → $LOCAL/fonts/enc/dvips/mplus1p/
  • mplus1p.map は既にある $LOCAL/fonts/map/dvips/mplus1p/mplus1p.map に追記する。このファイルは既に設定ファイルで有効化されているはず。
  • *.fd → $LOCAL/tex/latex/mplus1p/
  • 例によって「必要に応じて mk何とか」…。

そして、テスト文書を組版して正常に出力されれば完了である。

[test3.tex]
\documentclass{article}
%\pdfmapfile{+pdftex-mplus1p.map} % これは後述
\usepackage[scale=0.8]{geometry} % テキスト領域幅を大きくする
\usepackage[T1]{fontenc}         % T1 を既定にする
\usepackage{textcomp}            % TS1 の記号を使えるようにする
\begin{document}
\newcommand*\sampleText{% サンプルテキストをマクロにした
  ``Macros are \textsl{pass\'e} ---
  they're \emph{so} mid-20th-century.''
  \$1 = \texteuro 0.96 = \textyen 58} 
\fontfamily{mplus1p}
\fontseries{el}\selectfont \sampleText \par
\fontseries{l}\selectfont  \sampleText \par
\fontseries{m}\selectfont  \sampleText \par
\fontseries{sb}\selectfont \sampleText \par
\fontseries{b}\selectfont  \sampleText \par
\fontseries{eb}\selectfont \sampleText \par
\fontseries{ub}\selectfont \sampleText \par
\end{document}
f:id:zrbabbler:20110929030858p:image

(おまけ) pdfteX でも使えるようにする

ここまで OpenType を dvipdfmx で使うための作業を行ってきたが、実はこの作業を経た後なら、pdfTeX で使えるようにするのも簡単である。「手順の概略」をもう一度眺めてみると、追加で必要な作業は「pdfTeX 用のマップファイル」の作成だけであることが解る。ところで、otftotfm が出力する情報(一連の作業の中で、temp.map というファイルに保存した)は pdfTeX 用の書式であることは既に述べた。だから既に準備はできている。

[temp.map](ただし AutoEnc_... の長い識別子を省略して示した)
% Automatically maintained by otftotfm or other programs. Do not edit.

mplus1p-a-t1--base mplus-1p-thin "AutoEnc_op... ReEncodeFont" <[a_op3mbq.enc <mplus-1p-thin.ttf
mplus1p-ao-t1--base mplus-1p-thin "0.167 SlantFont AutoEnc_op... ReEncodeFont" <[a_op3mbq.enc <mplus-1p-thin.ttf
mplus1p-a-ts1--base mplus-1p-thin "AutoEnc_yh... ReEncodeFont" <[a_yh4sjf.enc <mplus-1p-thin.ttf
mplus1p-ao-ts1--base mplus-1p-thin "0.167 SlantFont AutoEnc_yh... ReEncodeFont" <[a_yh4sjf.enc <mplus-1p-thin.ttf
%(以下他のウェイトについて同様の記述)

ただし、このまま使うと一つ問題がある。現状の pdfTeX は OpenType フォントの場合には合成斜体をサポートしていないのである。これはどうしようもないので、斜体は諦めて、斜体の TFM も直立体と同じ字形で出力することで済ませる。上のマップの中で合成斜体の指示*1は「0.167 SlantFont 」の部分なので、この文字列を全て削除する。この修正を加えたファイルを pdftex-mplus1p.map とする。

[pdftex-mplus1p.map]
mplus1p-a-t1--base mplus-1p-thin "AutoEnc_op... ReEncodeFont" <[a_op3mbq.enc <mplus-1p-thin.ttf
mplus1p-ao-t1--base mplus-1p-thin "AutoEnc_op... ReEncodeFont" <[a_op3mbq.enc <mplus-1p-thin.ttf
mplus1p-a-ts1--base mplus-1p-thin "AutoEnc_yh... ReEncodeFont" <[a_yh4sjf.enc <mplus-1p-thin.ttf
mplus1p-ao-ts1--base mplus-1p-thin "AutoEnc_yh... ReEncodeFont" <[a_yh4sjf.enc <mplus-1p-thin.ttf
%(以下他のウェイトについて同様の記述)

このファイルを $LOCAL/fonts/map/pdftex/mplus1p/ に配置する。例によって(以下略)。あとは、pdftex-mplus1p.map が既定で読まれるように設定する…、のはずだがここで難題がある。pdfTeX が既定で読むマップファイルは pdftex.map であり、しかもこの動作は簡単には変えられない。*2従って、ここでは「追加のマップファイルを (La)TeX 文書中で指定する」という方法を用いる。実行コマンドが変わるわけではないのでこの方法であまり問題は生じないであろう。

LaTeX 上の設定自体は何も変わらないので、上述のテスト文書 test3.tex に先に述べた「マップファイル追加」の記述を追加する。つまり、2 行目のコメントアウトを外して有効化する。*3

\pdfmapfile{+pdftex-mplus1p.map} % pdfTeX で文書中でマップファイル読込を指定する。

そして、「pdflatex test3」を実行して、結果として上掲の dvipdfmx での結果と「斜体が効いていないこと」を除いて同じものが得られれば成功である。


これまでの作業で生成されるファイルを集めたアーカイブファイルを置いておく。

ただし、対象を広げていて、OT1/T1/TS1/LY1 のエンコーディングに対して直立体/斜体(合成;pdfTeX では未サポート)を対象としている。*4サポートする DVI ウェアは dvipdfmx と pdfTeX である。注意として、このアーカイブの中では、dvipdfmx 用のマップファイルの名前が mplus1p.map ではなく pdfm-mplus1p.map となっている。*5

※なお、ここで述べた方法が正常に動作しない環境が存在することが判明している。詳細については「できない件」を参照してほしい。

*1:Type1 フォントであればこの指示が有効になる。

*2:しかも pdftex.map は updmap に支配されているのでこれに追記を行うという方法も不適切である。実際には既定で読むファイルを追加することは可能である――pdftexconfig.tex\everyjob の設定を書き加える――が、これにはフォーマットのリビルドを要するので、少し敷居が高いように感じる。恐らくその方法が普通に用いられることは想定されていないのではないかと思う。

*3:ちなみに、\pdfmapline{+mplus1p-a-t1--base ...} のようにしてマップ行を設定することもできる。

*4:ちなみに、OT1 のエンコーディングファイルとして用いたのは fontname パッケージの 7t.enc である。

*5:私は慣習として dvipdfmx 用のマップファイルの名前を pdfm- で始めるようにしている。

2011-09-29

dvipdfmx で OpenType する件について (4)

前回の続き)

前回の一連の作業では、LY1 エンコーディングを用いた。LY1 は収録文字範囲の割と貧弱な(Latin-1 または CP1252*1程度をカバーする)フォントに対して用いるには最適である。しかし、「M+ フォント」は日本で制作されたフリーフォントとしては珍しくかなり広い欧文の収録範囲をもち、また配布ページにはわざわざ「T1 エンコーディングに完全対応する」旨が書かれているので、折角だから T1 を使いたい。他の理由として、LaTeX で LY1 を(他のエンコーディングと混ぜて)使うのは fontenc を「ちゃんと」使う必要があって若干高度な話になる。*2というわけで、今回は次の作業を行ってみる。

  • T1 および TS1 エンコーディングを採用する。*3
  • 自動変形による斜体のサポート。
  • (おまけ)pdfTeX でも使えるようにしてみる。

エンコーディングファイルの在り処

斜体は後回しにして、通常(直立体)のフォントについて、T1/TS1 用の TFM ファイルを otftotfm で生成することを考える。これについては、エンコーディングファイルが LY1 のもの(texnansi.enc)から T1/TS1 のものに変わるだけでよさそうである。しかし T1/TS1 の .enc ファイルは一体どこにあるだろうか。

  • TeX Live の場合は、Kpathsearch で探して*4見つかる ec.enc と q-ts1-uni.enc を用いるのがよいだろう。
  • W32TeX の場合、Kpathsearch で見つかる ec.enc *5は致命的に間違っているので、CTAN から fontname パッケージ(以前に述べた、Berry 規則の文書があるパッケージ)を入手して、その中の ec.enc と q-ts1-uni.enc(TeX Live にインストールされているのはこれと同じもの)を用いる。
  • ちなみに、今までちゃんと述べていないが、本来は、エンコーディングファイルを用意するのはずっと大変な作業である。エンコーディングファイルの中に書かれているのは「グリフ名(glyph name)」の列であるが、「同じグリフ(文字)」であっても、フォントによりグリフ名が異なる可能性がある。*6だから、本当なら、フォントのグリフイメージと名前の一覧を見て、「そのフォント専用」の T1 エンコーディングを構成する正しいグリフ名のリストを手作業で作る必要があるのである。*7では今までの作業で、「どこかから持ってきた .enc ファイル」で代用できているのは何故か。実は、otftotfm が「何か上手いことやって」指定した .enc ファイルから「そのフォント用の正しい .enc ファイル」(これが出力に含まれる a_??????.enc というファイルである)を作っているのである。*8だから、例えば Latin Modern フォントが用意している lm-ec.enc や lm-ts1.enc を使っても上手くいくかも知れない。結局のところ不確実でやってみないと解らない。*9

エンコーディングファイルさえ得られれば、あとは LY1 の時の作業と同じである。今回は後の作業のため、マップ情報をファイル temp.map に書き出しておく。まず Thin ウェイトについて otftotfm を実行する。

otftotfm --no-type1 --no-dotlessj -f kern -f liga --mapfile=temp.map -e ec.enc -n mplus1p-a-t1 mplus-1p-thin.ttf
otftotfm --no-type1 --no-dotlessj -f kern -f liga --mapfile=temp.map -e q-ts1-uni.enc -n mplus1p-a-ts1 mplus-1p-thin.ttf

斜体用の TFM の生成

「M+ フォント」には(多くの日本語フォントと同じく)「斜体」のフォントは用意されていない。しかし大抵のレンダラ/DVI ウェアには、フォントのもつ字形をその場で変形(斜体化と幅の伸縮)して出力する機能を持つ。ここではそれを利用して斜体のフォントを補ってみよう。合成斜体はレンダラの側の機能だから、TeX から見れば本物の斜体のフォントがあるのと同じ扱いのはずで、従って、斜体のフォント用の TFM が別に必要となり、さらに、それは直立体とは別のメトリックをもつはずである。otftotfm では -S--slantオプションを付けることで「合成斜体としての TFM」を作ることができる。

otftotfm --no-type1 --no-dotlessj -f kern -f liga --mapfile=temp.map -S 0.167 -e ec.enc -n mplus1p-ao-t1 mplus-1p-thin.ttf
otftotfm --no-type1 --no-dotlessj -f kern -f liga --mapfile=temp.map -S 0.167 -e q-ts1-uni.enc -n mplus1p-ao-ts1 mplus-1p-thin.ttf

ここで -S に指定する値は「水平変位÷垂直変位」(鉛直方向からの勾配値)である。*10TFM 名には斜体を表す Variant 値「o」を追加したものを用いた。

これで 1 つのウェイト(Thin)についての全てのシェープ(T1/TS1;直立/斜体)を生成し終えた。これを残りの 6 つのウェイトについて繰り返す。

dvipdfmx 用のマップファイルの作成

この段階で、temp.map は以下のようになっているはずである。

[temp.map]
% Automatically maintained by otftotfm or other programs. Do not edit.

mplus1p-a-t1--base mplus-1p-thin "AutoEnc_op... ReEncodeFont" <[a_op3mbq.enc <mplus-1p-thin.ttf
mplus1p-ao-t1--base mplus-1p-thin "0.167 SlantFont AutoEnc_op... ReEncodeFont" <[a_op3mbq.enc <mplus-1p-thin.ttf
mplus1p-a-ts1--base mplus-1p-thin "AutoEnc_yh... ReEncodeFont" <[a_yh4sjf.enc <mplus-1p-thin.ttf
mplus1p-ao-ts1--base mplus-1p-thin "0.167 SlantFont AutoEnc_yh... ReEncodeFont" <[a_yh4sjf.enc <mplus-1p-thin.ttf
%(以下他のウェイトについて同様の記述)

以前に述べたようにこれは pdfTeX 用の形式である。だからこれを dvipdfmx 用の形式に書き直す。dvipdfmx のマップにおいて、合成斜体を指定したい場合は、実フォント名の後に「-s〈値〉」を書くことになっている。従って、上で示した部分に対応する記述は次のようになる。

[mplus1p.map]
mplus1p-a-t1--base a_op3mbq.enc mplus-1p-thin.ttf
mplus1p-ao-t1--base a_op3mbq.enc mplus-1p-thin.ttf -s 0.167
mplus1p-a-ts1--base a_yh4sjf.enc mplus-1p-thin.ttf
mplus1p-ao-ts1--base a_yh4sjf.enc mplus-1p-thin.ttf -s 0.167

実際にはこの 7 倍の量の記述があるわけだから、手作業でやるのは大変である。何かスクリプトを書いた方が早くて確実だろう。

#!/path/to/perl
while (<>) {
  m/^ (mplus1p-\S+) \s+ \S+ \s+
    " (?:(\S+) \s+ SlantFont \s+)? AutoEnc_\w+ \s+ ReEncodeFont " \s+
    <\[ (a_\w+).enc \s+ <(mplus-\S+.ttf) $/x or next;
  $t = $2 && " -s $2";
  print "$1 $3 $4$t\n";
}

(うん、まだ続く…)

*1:Latin-1(ISO-8859-1)の Microsoft 独自拡張版。

*2LaTeX で無設定で使えるのは OT1/T1/TS1 だけだから、\usepackage[LY1]{fontenc} として LY1 を有効化する必要があるが、これだけだと既定が LY1 になるため「LY1 で使いたいフォント」以外にも影響が出てしまう。そこで、\usepackage[LY1,OT1]{fontenc} のようにオプション部の LY1 の後に「実際に既定にしたいもの」を改めて書く必要がある。

*3:Latin-1 にある「普通」の記号で T1 に入っていないものが TS1 にあるので、T1 をサポートするなら TS1 も含めるのが望ましい。

*4:つまり「kpsewhich ec.enc」を実行して表示されるパスのファイル。

*5:$TMF/fonts/enc/dvips/base/ec.enc。なお、同じディレクトリにある cork.enc は正しい T1 エンコーディング(の一種)のように見える。

*6:例えば T1 の符号位置 32 にある〈␣〉(U+2423)には visiblespace、visualspace、uni2423 等のグリフ名が使われている。もちろん頻出する文字については慣習的に定まっている(例えば〈Q〉→ Q、〈5〉→ five、〈#〉→ numbersign)。

*7TeX システムのインストールの中に「T1 エンコーディング」に相当するはずの .enc ファイルが大量に存在するのはそのためである。

*8:多分こんな動作をしているのだと思う:入力エンコーディングの中に visiblespace があったとする。otftotfm は「visiblespace は U+2423 を表すのに使われる」という知識を持っている。従って、入力の OpenType フォントの U+2423 に対するグリフを(Unicode cmap を使って)得て、そのグリフ名を調べて、その名前を出力のエンコーディングに書き込む。もし、該当のグリフがフォントになかった場合は一般には .notdef に置き換えられるが、確かこの文字の場合は VF を使って合成していたはずである。

*9:ちなみに、ttf2tfm は「エンコーディングの修正」をする機能がない。otftotfm でも .enc ファイルを -e--encoding)でなく --literal-encoding で渡すと修正が行われなくなる。

*10TFM の保持する SLANT 値と同じ定義。敢えて角度を持ち出して「θだけ傾ける」という場合は、tan θ の値ということになる。

2011-09-28

dvipdfmx で OpenType する件について (3)

前回の続き)

LaTeX サポートの作成

LaTeX サポートの要であるフォント定義ファイルの作り方については、「持ち込む話」で既に説明した。まずは、各フォントの対応先となる NFSS 属性値を決める必要がある。「M+ P Type-1」の場合、直立体のウェイトの異なる 7 つのフォントから構成されている。存在するウェイトを細→太の順に挙げると以下のようになる。*1

Thin(a), Light(l), Regular(r), Medium(m), Bold(b), Heavy(h), Black(c)

従って、シリーズ以外は「LY1/mplus1p/?/n」で固定で、シリーズ(「?」の部分)だけ異なることになる。シリーズの値として常用されるものを挙げる。(括弧なし→括弧一重→括弧二重の順で「よく使われる」)

(ul), (el), l, m, ((mb)), (sb), ((db)), b, eb, (ub)

この 2 つの属性値の対応を私は以下の方針で決めた。m は \mdseries が用いるシリーズ値であり、すなわち「既定」のシリーズである。従って、フォントファミリの中で、最も普通のウェイトをシリーズ m に充てるべきである。「Regular」というウェイトがある場合はそれが「最も普通」としていいだろう。*2残りについて、「よく使われるもの」を優先して使うことにした。その結果は以下の通りである。

Thin(a)Light(l)Regular(r)Medium(m)Bold(b)Heavy(h)Black(c)
ellmsbbebub

これで「設定すべき内容」が確定したので、定義ファイルを作ることができる。

[ly1mplus1p.fd]
\DeclareFontFamily{LY1}{mplus1p}{}
\DeclareFontShape{LY1}{mplus1p}{el}{n}{<->mplus1p-a-ly1}{}
\DeclareFontShape{LY1}{mplus1p}{l}{n}{<->mplus1p-l-ly1}{}
\DeclareFontShape{LY1}{mplus1p}{m}{n}{<->mplus1p-r-ly1}{}
\DeclareFontShape{LY1}{mplus1p}{sb}{n}{<->mplus1p-m-ly1}{}
\DeclareFontShape{LY1}{mplus1p}{b}{n}{<->mplus1p-b-ly1}{}
\DeclareFontShape{LY1}{mplus1p}{eb}{n}{<->mplus1p-h-ly1}{}
\DeclareFontShape{LY1}{mplus1p}{ub}{n}{<->mplus1p-c-ly1}{}

このファイルを $LOCAL/tex/latex/mplus1p/ に配置する。そして、実際に NFSS で LY1/mplus1p を指定する LaTeX 文書の組版を試してみる。

\documentclass{article}
\usepackage[LY1,OT1]{fontenc}
\begin{document}
\newcommand*\sampleText{% サンプルテキストをマクロにした
  ``Macros are pass\'e --- they're so mid-20th-century.''} 
\fontencoding{LY1}\fontfamily{mplus1p}
\fontseries{el}\selectfont \sampleText \par
\fontseries{l}\selectfont  \sampleText \par
\fontseries{m}\selectfont  \sampleText \par
\fontseries{sb}\selectfont \sampleText \par
\fontseries{b}\selectfont  \sampleText \par
\fontseries{eb}\selectfont \sampleText \par
\fontseries{ub}\selectfont \sampleText \par
\end{document}
f:id:zrbabbler:20110929030744p:image

これが正常に出力されれば、作業は無事完了である!

(もうちょっとだけ続くんじゃ…)

*1:括弧内は TFM 名の Weight で、こちらは単に Light や Bold 等の「名前」に対応するものを Berry 規則に従って当てればよい。各用語の用法について完全に定まっている訳ではないので、「細さの順番」については、実際の出力をみて判断しなければならない。

*2:「Regular」がない場合は「Medium」が「最も普通」とする。

2011-09-27

dvipdfmx で OpenType する件について (2)

前回の続き)

マップファイルの作成

dvipdfmx のマップの形式は実際に必要となる情報だけからなるので非常に単純である。

TFM名〉 〈エンコーディングファイル名〉 〈実フォントファイル名〉 [〈オプション〉]

先に示した pdfTeX のマップの内容をこの形式に直したファイルを作成する。

[mplus1p.map]
mplus1p-r-ly1--base a_6si6az mplus-1p-regular.ttf

この段階(ファイルが作業ディレクトリにある)で今までの作業が成功しているかを確認してみる。次のように「TeX レベルのフォント指定」を含む TeX 文書を用意する。*1(233 は文字〈é〉の LY1 での符号位置。)*2

[test.tex]
\documentclass{article}
\begin{document}
\font\testfont=mplus1p-r-ly1 \testfont
``Macros are pass{\char233} --- they're so mid-20th-century.''
\end{document}

これを dvipdfmx を用いて PDF に変換する。この時、先ほど作った mplus1p.map を -f オプションで読ませる。

latex test.tex
dvipdfmx -f mplus1p.map test.dvi

出力された test.pdf を見て正常であるか確かめよう。ビットマップになっていないかの確認も忘れずに。

f:id:zrbabbler:20110928020526p:image

ここまで成功だったら、残りの 6 つのフォントについて同様の作業を繰り返す。マップファイルは最終的に以下のような内容になるだろう。

[mplus1p.map]
mplus1p-a-ly1--base a_6si6az mplus-1p-thin.ttf
mplus1p-l-ly1--base a_6si6az mplus-1p-light.ttf
mplus1p-r-ly1--base a_6si6az mplus-1p-regular.ttf
mplus1p-m-ly1--base a_6si6az mplus-1p-medium.ttf
mplus1p-b-ly1--base a_6si6az mplus-1p-bold.ttf
mplus1p-h-ly1--base a_6si6az mplus-1p-heavy.ttf
mplus1p-c-ly1--base a_6si6az mplus-1p-black.ttf

各ファイルの配置

dvipdfmx のマップファイル以外は例の資料の 5.7 節に出てきているものなので、それと同様にすればよい。(「パッケージ名」を mplus1p とする。)

  • *.tfm → $LOCAL/fonts/tfm/public/mplus1p/
  • *.vf → $LOCAL/fonts/vf/public/mplus1p/
  • *.enc → $LOCAL/fonts/enc/dvips/mplus1p/*3

フォントファイル(*.ttf)については以下のように配置する。

後の方法について補足しておくと、一般的な OpenType フォント(*.otf/*.ttf)の場合、CFF グリフの場合は $LOCAL/fonts/opentype/ 以下、TrueType グリフの場合は $LOCAL/fonts/truetype/ 以下に配置する。(拡張子が .otf/.ttf であるかは関係ないことに注意。)*4

最後に、dvipdfmx のマップファイル mplus1p.map は以下のように配置する。

  • マップファイル(*.map)自身は $LOCAL/fonts/map/dvipdfmx/mplus1p/ に置く。
  • これだけでは、既定でこのマップが読み込まれるようにならず、*5そうさせるには「設定ファイル」にその為の記述が必要である。この辺りの仕様は記事「updmap はなぜ動くのか」において説明した。それに従うと、設定ファイル $TMF/dvipdfmx/config/dvipdfmx.cfg の末尾に
    f mplus1p.map
    
    を追記すればよい。

なお、例によって、TEXMF ツリーにファイルを追加した際には「必要ならば mktexlsr を実行する」が必要になる。

先ほどの test.tex を空のディレクトリに置き、そこで「latex test.tex」→「dvipdfmx test.dvi」(今度は -f なし)を実行して正しい PDF が生成されればここまでの作業は成功である。

(まだ続きます)

*1:ここでは「TeX 混じりの LaTeX 文書」にしたが、無論 plain TeX でも構わない。

*2LaTeX で通常用いられる \'e のような「アクセント命令」は NFSS の枠組を前提にしている。だから、この例のように「TeX レベルでフォントが変更されている」状況で用いると何が起こるか解らない。

*3:「dvips」であることに注意。

*4:「otfinfo -t」でテーブル一覧を表示し、中に「CFF 」があれば CFF グリフ、「glyf」があれば TrueType グリフである。なお、「拡張子が .ttf なら TrueType グリフ」は成立するが、逆は必ずしも成立しない。

*5-f オプションで明示指定した場合は読み込まれる。

2011-09-26

dvipdfmx で OpenType する件について (1)

今回は、OpenType フォント(TrueType フォントを含む*1)を LaTeX + dvipdfmx で使えるようにする手順を、その原理の説明を含めて解説することにする。インストール対象のフォントとして、「M+ P Type-1 ファミリ」を用いる。

手順の概略

今回述べる「dvipdfmx に対する設定」を dviout や updmap に対するそれと比べると、対象のソフトウェアが異なるので色々な作業の手順に違いが出る。しかし重要なことは、作業の全体的な流れは変わることがないということである。

  1. TFM ファイル(.tfm)とその付随ファイル(.enc/.vf)の生成。
  2. ソフトウェア用のマップファイルの作成。
  3. 各ファイルの配置。
  4. LaTeX レベルの支援ファイルの作成・配置。

例の資料での事例と比較して、特に注意すべき点を挙げる。

  • 1. について: 実ファイルが OpenType だから、otftotfm を使うことになる。(「M+ P Type-1」は「TrueType フォント」なので、ttf2tfm も使用可能であるが、otftotfm の方が高機能である。)
  • 2. について: この場合、「レンダラ」に相当するのは dvipdfmx 自身である。*3従って、dvipdfmx 用のマップファイルを作成する必要がある。
  • 3. について: dvipdfmx の設定ファイルとマップファイルの場所について知る必要がある。
  • 4. は「TeX 以上のレベル」の話なので、実フォントや「レンダラ」の種類には全く依存しない。
  • 今回は LY1 エンコーディングを用いることにする。TFM の命名は「ZR 命名規則(謎)」に従う。NFSS や TFM 名でのファミリの名前は mplus1p とする。

今回の作業対象となるフォントの情報を以下にまとめる。*4

フォントファイル名TFMNFSS 指定
M+ 1p thinmplus-1p-thin.ttfmplus1p-a-ly1LY1/mplus1p/el/n
M+ 1p lightmplus-1p-light.ttfmplus1p-l-ly1LY1/mplus1p/l/n
M+ 1p regularmplus-1p-regular.ttfmplus1p-r-ly1LY1/mplus1p/m/n
M+ 1p mediummplus-1p-medium.ttfmplus1p-m-ly1LY1/mplus1p/sb/n
M+ 1p boldmplus-1p-bold.ttfmplus1p-b-ly1LY1/mplus1p/b/n
M+ 1p heavymplus-1p-heavy.ttfmplus1p-h-ly1LY1/mplus1p/eb/n
M+ 1p blackmplus-1p-black.ttfmplus1p-c-ly1LY1/mplus1p/ub/n

以下の作業はかなり多くのファイルを扱うので、専用の作業ディレクトリを作ってそこで行うことを推奨する。

TFM(+ 付随)ファイルを生成する

M+ フォントのダウンロードページからアーカイブを入手し、上掲の表に載っているファイルを作業ディレクトリに配置する。*5全部で 7 個のフォントについて作業することになるが、作業内容はほとんど同じなので、以下では「M+ 1p regular」についてのみ手順を示す。他のフォントについては、上の表を参照して必要な部分を読み替えるだけである。*6

それでは、実際に otftotfm で TFM ファイルを生成することにする。以下のコマンドを実行する。

otftotfm --no-type1 --no-dotlessj -f kern -f liga -e texnansi.enc -n mplus1p-r-ly1 mplus-1p-regular.ttf

オプション引数の意味は以下の通り。

  • --no-type1 --no-dotlessj: これは「Type1 フォントへの変換を行わない」ということ。*7
  • -f kern -f liga: OpenType 特性の kern と liga を有効にする。つまりカーニングとリガチャの情報を TFM に含める。*8
  • -e texnansi.encエンコーディングファイルの指定。厳密にいうと「基底の」エンコーディングの指定である。
  • -n mplus1p-r-ly1TFM 名の指定。
  • mplus-1p-regular.ttfフォントファイルの指定。

これを実行すると、次のファイルが生成される。

  • mplus-1p-ly1.tfm: 目的の TFM ファイル。
  • mplus-1p-ly1.vf: 生成された mplus-1p-ly1 は仮想フォント(VF)として実現されているので、.tfm とともに .vf ファイルが存在する。
  • mplus-1p-ly1--base.tfm: 仮想の mplus-1p-ly1 が参照している TFM。レンダラ/DVI ウェアでマップを設定する対象はこちらの TFM になる。*9
  • a_6si6az.enc: 「既定の」エンコーディングをカスタムして生成されたエンコーディングファイル。*10

ちなみに、例の資料にあるように「--mapfile=foo.map」のオプションを付けて実行すると、以下の内容の foo.map が生成されるはずである。*11

mplus1p-r-ly1--base mplus-1p-regular "AutoEnc_6si6azkxsy3cpk3ogugnfii5wf ReEncodeFont" <[a_6si6az.enc <mplus-1p-regular.ttf

これは pdfTeX 用のマップファイルである*12から、このままでは dvipdfmx では使えないが、これを見ると以下のことが判る:DVI ウェアのマップファイルで設定すべき内容は、「mplus1p-r-ly1--base」という TFM を「a_6si6az.enc」のエンコーディングで実のフォントファイル(mplus-1p-regular.ttf)にマップすることである。*13

(続きます)

*1:規格の定義上は「TrueType フォントは全て OpenType フォントである」が成立すると考えている。現在では多くの場合、「TrueType フォント」は「TrueType グリフ(glyf テーブル)をもつ OpenType フォント」のことを指す。ただし「TrueType フォント」と「OpenType フォント」が区別して扱われている場合に、どういう基準で区別するかは人によって異なることがあり、これが混乱を招くことがある。

*2:つまり、「印刷された紙」でないということ。紙に印刷するのなら、ビットマップフォントが埋め込まれてもあまり問題はない。

*3:ただし、dvipdfmx はフォントファイルに含まれるアウトライン(輪郭曲線)データをそのまま PDF 文書に含ませていて、ビットマップイメージを生成するという意味の「レンダリング」は行っていない。

*4TFM 名の「シェープ」部分については、ウェイトを表す「thin」「light」…の語を Berry 規則に従って変換したものである。NFSS のシリーズの決定については後述する。

*5:別の場所にこのフォントがあるならシンボリックリンクでも構わない。

*6:取り敢えず 1 つのフォントについて作業して成功したことを確認してから他のフォントの作業をするといいかも知れない。

*7:現状の otftotfm は TrueType グリフのフォントについては Type1 フォントへの変換はサポートされていないから、実際にはこのオプションは指定不要ではある。--no-dotlessj についてもう少し詳しく話すと:otftotfm には文字 dotlessj (U+0237) のグリフを文字 j のグリフを改変して(Type1 形式で)生成する機能を有している(やはり TyueType グリフのフォントには未対応)が、このオプションはその機能を抑止する。要するに、グリフデータの変換は不用意にやってほしくないのである。

*8:これは実用ではほぼ必須になる。ちなみに、clig、sups、ss01 等の他の特性を有効化することもできる。

*9:「vftovp mplus-1p-ly1 mplus-1p-ly1 mplus-1p-ly1」で mplus-1p-ly1.vpl を出力し、その中の MAPFONT 要素を見れば、実際に mplus-1p-ly1--base が参照されていることが確認できる。

*10:ファイル名は自動生成なので、環境によりこれと異なるかも知れない。

*11:foo,map が既に存在する場合は末尾に追記される。オプションを付けない場合はこの内容が画面に出力されているはずである。

*12:Type1 への変換が有効な場合は、dvips/updmap でも使える内容になる。

*13AutoEnc_... が気になる人は a_6si6az.enc の中を眺めてみよう。この文字列はこのファイルで定義された「エンコーディングデータ」の識別子である。ファイル名を指定しているのに何故わざわざ識別子が要るのかを理解するには Postscript の知識が必要である。後で見るように dvipdfmx ではこの識別子をマップファイルに書く必要がない。

2011-09-25

BXattritta を使ってみる

BXattritta パッケージインストール法に絡めて updmap の解説をしたが、肝心のパッケージの使用法をまだ述べていなかったのでここで書いておく。

「和文ゴシック + 欧文サンセリフ」を「マルベリ + Attritta」にする例。bxattritta パッケージに sfgt オプションを付けるとサンセリフ\sffamily)の既定が Attritta になる。さらに noalphabet 指定の pxchfon で和文のゴシックを「マルベリ」にすればよい。

% pLaTeX 文書
\documentclass[a4paper]{jsarticle}
\usepackage[noalphabet]{pxchfon}  % pxchfon は和文のみに適用
\setminchofont{ipam.ttf}          % \mcfamily → IPA明朝
\setgothicfont{MTLmr3m.ttf}       % \gtfamily → マルベリ
\usepackage[sfgt]{bxattritta}     % \sffamily → Attritta
\begin{document}                  % \rmfamily は CM Roman のまま
BXattritta パッケージの使用例です。\par
\sffamily
IE 11以上、Firefox 14以上、Chrome 37以上を推奨。
\end{document}

f:id:zrbabbler:20110925202540p:image

先のものはエンコーディングを(LaTeX 既定の)OT1 のままにしていた。T1、TS1 エンコーディングを使うには、それらを使う設定を普段通り行えばよいだけである。

% pLaTeX 文書
\documentclass[a4paper]{jsarticle}
\usepackage[T1]{fontenc} % T1 を既定にする
\usepackage{textcomp}    % TS1 の記号を使えるようにする
\usepackage[noalphabet]{pxchfon} % あとは同じ
\setminchofont{ipam.ttf}
\setgothicfont{MTLmr3m.ttf}
\usepackage[sfgt]{bxattritta}
\begin{document}
\sffamily % \textyen は TS1 で提供される
このボールペンは900円もした。\par
Ce stylo \`a bille m'a co\^ut\'e au moins de \textyen 900.
\end{document}

f:id:zrbabbler:20110925202539p:image

「丸ゴシック」を「ゴシック」と別に使いたい場合は、OTF パッケージの deluxe オプションで和文丸ゴシック\mgfamily)を使えるようにした上で、bxattritta を rsmg オプション付で読み込む。これで「欧文丸ゴシック」用の総称ファミリ \rsfamily が新設され、これが \mgfamily と連動するようになる。

\documentclass[a4paper]{jsarticle}
\usepackage[noalphabet]{pxchfon}
\usepackage[deluxe]{otf}
\setminchofont{ipam.ttf}        % \mcfamily → IPA 明朝
\setgothicfont{ipag.ttf}        % \gtfamily → IPA ゴシック
\setmarugothicfont{MTLmr3m.ttf} % \mgfamily → マルベリ
\usepackage[rsmg]{bxattritta}   % "\rsfamily" を Attritta にする
\begin{document}
\rmfamily 明朝 + Serif Family \par
\sffamily ゴシック + Sans-serif Family \par
\rsfamily 丸ゴシック + Rounded-sans Family \par
\end{document}

f:id:zrbabbler:20110925202538p:image

2011-09-24

TFM 名(Berry 命名規則)と NFSS の属性の関係

TFM 名(Berry 命名規則、あるいは「ZR 命名規則(謎)」*1)の Weight、Variant、Width の属性は NFSS のシリーズ、シェープの属性と似た概念であるが、前者は「元のフォント名で称されているもの」、後者は「実際に LaTeX でどの属性値を使いたいか」によって決められる。従って、両者の間に固定された機械的な対応はない。例えば、「AAA Demibold」と「BBB Semibold」という(別ファミリの)2 つのフォントがあるとすると、これらの TFM 名は aaa-d-ly1(Weight は d(Demi))と bbb-s-ly1(Weight は s(Semibold))となるが、LaTeX サポートを行う者が「両方ともシリーズ sb (Semi-bold)でよい」と考えたなら、NFSS 属性は LY1/aaa/sb/n と LY1/bbb/sb/n となるだろう。あるいは、あるファミリにウェイト「Demi」しかない場合は、TFM 名の Weight は d(Demi)としつつ、LaTeX では「普通の」シリーズ m(Medium)に対応させるのが適当かも知れない。

NFSS の属性値については、「設定する人に任せられる」という性質のため、Berry 規則のような「規定文書」が存在せず、完全にコミュニティの慣習に任せられている。だから「どういう値を用いればよいか解らない」と戸惑うこともあるだろう。そこで、「実際に用いられる NFSS 属性値の例を示す」ことと「『機械的な』TFM と NFSS の対応の例を示す」ことを目的として、以下では fontinst パッケージで用いられている対応を挙げることにする。「TFM」の欄が「—」なのは、「fontinst では対応が与えられていないが、用いられることのある NFSS 属性値」である。

[TFM Weight + Width ←→ NFSS シリーズ]

Weight に対応する「シリーズ(1)」と Width に対応する「シリーズ(2)」を順に並べたものが「シリーズ」の値となる。ただし両方が「m」の場合は片方を略して「m」をシリーズとする。例えば aaa-brx-ly1 *2は LY1/aaa/bx/n、aaa-r-ot1 は OT1/aaa/m/n となる。

TFM WeightNFSS シリーズ(1)
aul (Ultra Light)
jel (Extra Light)
ll (Light)
r, km (Medium)
mmb (Medium-bold)
ddb (Demi-bold)
ssb (Semi-bold)
bb (Bold)
h, c, xeb (Extra Bold)
uub (Ultra Bold)
TFM WidthNFSS シリーズ(2)
o, uuc (Ultra Condensed)
qec (Extra Condensed)
c, p, nc (Condensed)
sc (Semi-Condensed)
rm (Medium)
sx (Semi-Expanded)
e, xx (Expanded)
vex (Extra-Expanded)
ux (Ultra-Expanded)
[TFM Variant ←→ NFSS シェープ]

例えば、aaa-bo-t1 なら T1/aaa/b/sl となり、aaa-b-t1 なら Variant は r だから T1/aaa/b/n となる。シェープの扱いはあまり標準化されていないので、例えば aaa-ric-t1 のような「複数の Variant の指定」(Small Caps + Italic)を含む場合が判然としないのだが、この場合、scit のように単純に複数の要素を並べたシェープ値を用いる(つまり T1/aaa/m/scit とする)ことが多い(慣習として、scit であり itsc でない)。その他の(Berry 規則の Variant 値がある、あるいはそれ以外の)Variant もシェープとして扱われ、その場合の値は LaTeX パッケージ作成者によりまちまちである。*3

TFM VariantNFSS シェープ
rn (Normal)
csc (Small Caps; スモールキャピタル)
osl (Slanted; 斜体)
iit (Italic; イタリック)
ui (Upright Italic; 直立イタリック)
ol (Outline; 白抜き)

*1:以前の記事で紹介したように、Berry 規則の Weight、Variant、Width の指定法は ZR 規則にそのまま受け継がれる。この記事の以下の例では TFM 名を ZR 規則の命名で示す。

*2:Width が r 以外で Variant がない(つまり r)の場合は Variant の r が残る。aaa-bx-oy1 だと x は Variant の値となってしまう。本来の Berry 規則ではエンコーディングが Variant の一種と扱われ …b8yx となるので Variant の r はあまり出現しないのだが。

*3:大抵は、「そのパッケージ特有の高水準命令」を用意してそれで隠蔽してしまうので、それで問題になることがない。

2011-09-23

オレオレ命名規則について語ってみる

私自身は、TFM の名前を決める時に、Berry 命名規則ではなく、それを少し変更した「ZR 命名規則(謎)」を用いている。これについての経緯は以前の記事で説明した。拙作のパッケージの中の TFM の名前の付け方が気になる人*1のために、この命名規則についての説明をしておく。

「ZR 命名規則(謎)」は以下のような書式をとる。[ ] 内は省略可能である。

[r-]〈ファミリ〉-〈シェープ〉[-〈エンコーディング〉][-〈サイズ〉]

〈 〉内の要素は英小文字と数字のみで構成され、以下のように決められる。

命名の例

例えば、「持ち込む話」で扱った、「Plasma Drip BRK」(ファミリ名 plasma、エンコーディング LY1 で用いる)の場合:

  • 〈ファミリ〉は LaTeX のファミリに合わせて plasma とする。
  • 〈シェープ〉は Berry 規則に従って決める。この場合、元のフォント名に(Berry 規則の)Weight、Variant、Width を表現する単語が含まれていないので、この 3 つは全て r(regular)となる。Variant と Width がともに r の場合はそれらを省くと決められているので、結果的に〈シェープ〉は r となる。
  • エンコーディング〉は LY1 を小文字に直して ly1。(ちなみに、Berry 規則だと 8y となる。)
  • オプティカルサイズでないので〈サイズ〉はなし。
  • 従って、結果は plasma-r-ly1 となる。

「持ち込む練習」で扱った「CM Sans Serif Quotation Style」(cmssq8)および「〜 Slanted」(cmssqi8)に新たに ZR 命名規則適用するとしたら*8

  • 〈ファミリ〉を cmssq とする。(「CM Sans Serif Quotation Style」全体をファミリと扱っている。*9
  • cmssq8 の〈シェープ〉は: Weight が r、Variant が r、Width が r、従って全体で r。
  • cmssqi8 の〈シェープ〉は: Weight が r、Variant が o、Width が r、従って全体で ro。
  • エンコーディング〉は ot1。
  • もしオプティカルサイズとして扱うなら、〈サイズ〉を 8 とする。
  • 従って、cmssq8 は cmssq-r-ot1-8、cmssqi8 は cmssq-ro-ot1-8 となるはずである。

*1:ひょっとすると本当にいたりして?

*2:和文フォントで原メトリックである、あるいは仮想フォントで一部グリフを補う場合の元の「一部欠落した」もの、等。

*3:だから 5ߝ9 の数字が余る。「9」を「和文の(OTF パッケージの意味の)expert」に割り当てている。

*4:CMap の(和文)UnicodeLaTeX の名前を使う(jy2、j20、j32 等)。

*5Unicode.sfd は BMP のみをカバーする。

*6:私は勝手に「4096 サブフォント」と呼んでいる。

*7整数のみを想定している。

*8:無論この場合は TFM 名が先に決まっているので、そうする意味は全くない。

*9:もし、CM フォント全体を再命名するという場合、「CM Sans Serif」をファミリ(Berry 規則の Family)として「Quotation Style」を(Berry 規則の)Variant と扱うほうが自然かもしれない。(でもそういう属性値はないので結局できない?)さらに、CM フォントのように、同じ「ファミリ名」でセリフとサンセリフの両方があるという場合に対応できるように、Sans(s)や Typewriter(t)を Variant として用意している。

2011-09-22

Berry 命名規則とオレオレ命名規則

「持ち込む話」の中で Times のファミリを TeX で扱う例を載せているが、そこで登場する TFM の名前は次のようになっている。

  • ptmbi8t = T1, Times Bold Italic
  • ptmbi7y = LY1, Times Bold Italic
  • pplbi8t = T1, Palatino Bold Italic
  • ptmri8t = T1, Times Italic
  • ptmb8t = T1, Times Bold

これは、各々のフォントを「Berry 命名規則Berry naming scheme)」に従って名付けたものである。このような名前は、LaTeXフォントインストールについて調べたことのある人ならきっと見覚えがあるだろう。これは当該のフォントの「属性」の集まりを極めて短い文字列の形に表現したものである。例えば、「ptmbi8t」は次のような属性値を表している。

ptmbi8t
AdobeTimesBoldItalicT1
(Supplier)(Family)(Weight)(Variant)

この「変換規則」の記述等を含む、Berry 命名規則の仕様そのものについては CTAN の fontname パッケージに含まれる文書 fontname.html に述べられている。ただその資料にはこの規則の目的や適用基準などについては触れられていない。そこで、その辺りについて、私見を大いに交えて話をしたい。

Berry 命名規則が創案された背景

Berry 名が可読性に欠けると感じる人は多いであろう。TeX コミュニティにおいて長らくこのような名雨が使われていた理由については UK TeX FAQ の解説ページで説明されている。要するに、ファイル名を「8+3 形式」(MS-DOSISO 9660*1の制限に従った、ベース名 8 文字 + 拡張子 3 文字の名雨)に適合させるという絶対的な要求の中で、 最大限に可読性を担保しようと努力した結果が Berry 命名規則というわけなのである。(つまり Berry 名でない 8+3 なファイル名はもっと不可読であった。)もちろん、MS-DOSISO 9660(CD-ROM 自体が?)も全く過去のものとなった現状*2では、この「訳のわからない」名前を用いることの合理性は甚だ低い。

どういう場合に Berry 命名規則に従う必要があるのか?

不安に思っている人がいるかも知れないので確認しておく。TeXLaTeX のシステム自体は Berry 命名規則には全く依存しない。つまり、TFM にどんな名前を付けても、その名前で設定を行う限りは正常に動作する。さらに言うと、afm2tfm 等の(低水準の)TFM 生成ツールを直接使って新しいフォントTeXLaTeX に導入する作業を行う作業の中でも、Berry 命名規則が要請される個所は存在しない。*3ただし、(これらのツールを内部で使う)フォントインストール作業を自動化するツール(CTAN を見るといっぱいあります)の中には、Berry 命名規則の使用を前提とするものがあるかも知れない。(LaTeX 組込まで自動化するものであればそもそも TFM 名を使用者が気にする必要もないはずだが。)

新しいフォントインストールする際に Berry 命名規則に従ったほうがよいか?

Berry 命名規則に従うべきかの最終的な判断については、これを読んでいる各人に任せる。ただ注意すべきことは、選択の余地がない場合が多いということである。fontname.html のファミリの対応表を見れば解るが、そもそも Berry 規則では「予め決められたもの」しか命名できないのである。だから、例えば「持ち込む話」 で「Plasma Drip BRK」のフォントに対して単純に plasma という TFM 名を与えたが、「Berry 名は何か」と聞かれても適当な答えが存在しない。要するに、大概の場合には Berry 命名規則は「使えない」、と私は判断している。

ZR 命名規則(謎)

とはいっても、やはり命名規則はあった方がよいと私は思っている。そして命名は次の性質を満たしてほしい。

  • 自分が使いそうな属性値(和文のファミリやエンコーディングなども含む)に対応できる。
  • 極端に長すぎない(20 文字以内に収まる)。
  • 自分が使いこなせる/覚えられる。

というわけで、私自身は以下のような命名規則を用いることにしていて、これを「ZR 命名規則(謎)」と呼んでいる。*4

[r-]〈ファミリ〉-〈シェープ〉[-〈エンコーディング〉][-〈サイズ〉]
([ ] 内は省略可能)

この規則は、自分が公開しているフォントパッケージの中でも一貫して用いられている。例えば、PXmika パッケージの中身を見ると、mika-r-jt1 や r-cira-r-ot1 や r-mika-r-u30 といった名前の TFM ファイルが存在する。

もう少し詳しく説明しようかとも思ったが、確かこの記事の主題は Berry 規則だったはずなので、またの機会に回すことにする。

*1CD-ROMファイルシステムの標準仕様

*2:Karl Berry 氏自身も「8+3 にする必要はもうない」と言っている。(参考)ちなみに、この TUG Interview で彼が「Berry 命名規則」に言及しているところも面白い。

*3:otftotfm の man page にはわざわざ「Berry 命名規則に従う必要はない」という注意書きがされている。

*4:この方式に転換する前には「拡張 Berry 規則」みたいのを 2 年ほど使っていた時期がある。「Berry 規則と同様に読みにくく、しかも Berry 規則と微妙に互換性がない」とメリットが全然ないので結局放棄した。;-)

2011-09-21

LaTeX に新しいフォントを持ち込む練習

「マルベリでないフォントの話」で、CM Sans Serif Quotation Style の話をして、それが既定では NFSS でサポートされていないということを述べた。「LaTeX へのフォントの持ち込み」の練習として、これをファミリ cmssq で使えるようにしてみよう。要件は以下の通り。

  • 次の 2 つのフォントTFM)が存在する。エンコーディングは OT1。
    • cmssq8 : CM Sans Serif Quotation Style
    • cmssqi8 : CM Sans Serif Quotation Style Slanted
  • これらのデザインサイズは 8pt であるが、他のデザインサイズのものは存在しないので、「オプティカルサイズ」でない(全てのサイズで相似の字形を用いる)フォントとして扱う。
  • \upshape(直立体)を cmssq8、\slshape(斜体)を cmssqi8 に対応させる。
  • さらに、\itshape(イタリック)が指定された場合は斜体で代用する。

\upshape に対応する NFSS のシェープの値は n、\slshapesl\itshape は it である。*1

「持ち込む話」の教えに従えば次のようなファイル ot1cmssq.fd を作るところまではできる。*2

[ot1cmssq.fd]
\DeclareFontFamily{OT1}{cmssq}{}
\DeclareFontShape{OT1}{cmssq}{m}{n}{<->cmssq8}{}
\DeclareFontShape{OT1}{cmssq}{m}{sl}{<->cmssqi8}{}

あと必要なのはイタリック(OT1/cmssq/m/it)を斜体(OT1/cmssq/m/sl)で「代替する」という設定だけである。OT1/cmssq/m/sl と同じにするので

\DeclareFontShape{OT1}{cmssq}{m}{it}{<->cmssqi8}{}

でいいと思うかもしれない。実際のところはこれで何の問題もなく動作するのであるが、「代替する」ということを明確に表すための書き方がありそれを使うのが通例である。TFM 名の代わりに ssub*<代替先> のように書く。*3これを用いると次のように書ける。

\DeclareFontShape{OT1}{cmssq}{m}{it}{<->ssub*cmssq/m/sl}{}

もしこれを書かないと、現在のフォント指定が OT1/cmssq/m/it になった場合には警告が表示されて OT1/cmssq/m/n で代替される。*4他の設定値についても、警告が出るのが嫌なら明示的な代替設定を加えておけばよい。といっても有りとあらゆる値に対して代替設定することは無理なので適当な落とし所をとることになる。例えば、シリーズが bx(太字;\bfseries)になることは頻出するので、これは明示設定(同じシェープの m シリーズで代替)した方がいいかも知れない。

\DeclareFontShape{OT1}{cmssq}{bx}{n}{<->ssub*cmssq/m/n}{}
\DeclareFontShape{OT1}{cmssq}{bx}{sl}{<->ssub*cmssq/m/sl}{}
\DeclareFontShape{OT1}{cmssq}{bx}{it}{<->ssub*cmssq/m/it}{}

それでは、完成した ot1cmssq.fd を配置(カレントでよい)した上で CM Sans Serif Quotation の例として挙げた「The TeXbook」の引用句の組版LaTeX で再現してみよう。*5

\documentclass[a4paper]{article}
\begin{document}
\begin{flushright}
% OT1/cmssq/m/it の 8pt(行送り 10pt)を指定
\fontfamily{cmssq}\slshape\fontsize{8}{10}\selectfont
They do certainly give\\
very strange and new-fangled names to diseases.
\par\smallskip
\upshape  % 次行の「書名」部はわざと \textsl でなく \textit にしてみる
--- PLATO, \textit{The Republic}, Book 3\enskip (c.\ 375 B.C.)
\end{flushright}
\end{document}

無事に件の記事の画像と同じものが出力されれば成功である。

\DeclareFontShape の「TFM 名」の箇所には色々な指定があるが、本当に TFM 名を書く形式と「ssub*」形式が実際の使用の大多数を占めると思う。*6これ以外で時々使われるのがスケール指定を伴う TFM 指定である。例えば、cmssq8 を「実際に指定されたサイズの 95 %で出力される」ようにしたい場合は次のような書き方をする。

\DeclareFontShape{OT1}{cmssq}{m}{n}{<->s*[0.95]cmssq8}{}

この場合、OT1/cmssq/m/n の 10pt を指定した場合、cmssq8 at 9.5pt が使われることになる。

*1:これはマクロ \updefault の値を調べれば解る。つまり、このマクロの定義を変えればこれらの総称命令の意味を変えることが可能であるが、実際にはそういうことはまず行われない。

*2:シリーズは 1 つしかないので m でいいだろう。デザインサイズの値は関係しない。

*3代替先はシェープから書き始める。エンコーディングが異なるものは代替できるはずがないからである。

*4:この暗黙の代替先の指定は \DeclareFontSubstitution{OT1}{cmr}{m}{n} という指定が為されているからである。つまり、OT1 で無効な指定(OT1/cmssq/m/it)があった場合は、シェープを n に変える(OT1/cmssq/m/n)。今はここで有効な指定が得られたが、仮にこれも無効だったら今度はシリーズを m に変えて、それも無効ならファミリを cmr に変える。それでもダメなら…「存在しないものを暗黙の代替にしている」ことになってしまうので「NFSS が変だ!」というエラーが出る。

*5:勿論 Knuth が使っているのは plain TeX である。

*6:全容に関心のある人は「texdoc fntguide」を実行してみよう。

2011-09-20

updmap はなぜ動くのか (2)

前回の続き)

設定ファイルとマップファイルの関係

updmap の動作原理を見る前に、各ソフトウェアについて、マップファイルがどのように読み込まれているかを調べる。ここでは例として dvipdfmx を用いる。

dvipdfmx が和文フォントの設定に「cid-x.map」というファイルを用いていることはよく知られている。すなわち、$TMF/fonts/map/dvipdfmx/base/cid-x.map というファイルを dvipdfmx は読み込んでいるのであるが、これは dvipdfmx で最初から規定された動作ではない。それは dvipdfmx の設定ファイル $TMF/dvipdfmx/config/dvipdfmx.cfg を見ればわかる。

[dvipdfmx.cfg; 末尾]
%%
%% Font map files
%%
f psbase14.map
%f dlbase14.map
%f embase14.map
%f kbbase14.map
%f pdfmomegaj.map
% w32tex
f pdfmfnt.map
%
f cid-x.map

つまり、設定ファイルにおいて cid-x.map の読込が指示されているのである。*1dvips も同様の「設定ファイルでマップファイルを指定する」構造になっている。ただこれに対して、「単一の固定のマップファイルを常に読み込む」ソフトウェアもあり、例えば ttf2pk がそうであり、pdfTeX も(事実上)そうである。

updmap は「設定ファイルでマップファイルを指定する」方式をとる。設定ファイルは $TMF/web2c/updmap.cfg である。

[updmap.cfg; 末尾]
# cm, amsfonts, latin modern and fpl (Do not erase the next lines).
Map cm.map
Map cmextra.map
Map cyrillic.map
Map euler.map
Map latxfont.map
Map symbols.map
Map bakoma-part.map
Map lm.map
Map fpls.map
#
Map bxattritta.map

ここで、最後の行は、先ほど「updmap --add bxattritta.map」を実行したこどで追加されたものである(既定のものにはない)。つまり、updmap の --add オプションはこの設定ファイルを自動更新するためのものである。

updmap の動作原理

updmap の組み込んだシステムでも、各 DVI ウェアの動作自体は何も変わらないので、updmap.cfg 自体を dvipdfmx 等が見ている訳ではない。それではどうやって updmap は各ソフトウェアの設定を変えているのか。答えは簡単で、各ソフトウェアの設定ファイルによって読み込むことが決まっているマップファイルの内容を書き換えているのである。例えば、dvipdfmx の場合、前掲の dvipdfmx.cfg に記述されているマップファイル $TMF/fonts/map/dvipdfm/updmap/pdfmfnt.map*2が書き換えられる。事実、このファイルの末尾は以下のようになっている。

[pdfmfnt.map; 末尾]
attr-r-ot1 default attr-r-ot1
attr-r-t1 default attr-r-t1
attr-r-ts1 default attr-r-ts1

これは、前回示した bxattritta.map の内容を dvipdfmx のマップファイルの書式に書き直したものである。もちろんこの変換は updmap が自動で行っている。

W32TeX の updmap は以下に挙げるファイルを更新する。

  • $TMF/fonts/map/dvips/updmap/dvipsfnt.map: dvips 用のマップファイル。追加設定ファイル $TMF/dvips/config/config.bi 等で参照される。*3また gsftopk でも使われる($TMF/dvips/config/config.gsftopk で参照)。
  • $TMF/fonts/map/dvipdfm/updmap/pdfmfnt.map: dvipdfmx 用のマップファイル。設定ファイル $TMF/dvipdfmx/config/dvipdfmx.cfg で参照される。
  • $TMF/fonts/map/pdftex/updmap/pdftex.map: pdfTeX 用のマップファイル。このファイル名は pdfTeX の既定値である。*4
  • $TMF/fonts/map/ps2pk/updmap/pspksupp.map: W32TeX の mktexpk が ps2pk を用いて処理するために使うファイルらしい。*5ファイル名は固定。

ひと段落

整理すると、以下のようになる。

  • 単に updmap を実行すると、現状の設定ファイル updmap.cfg に書かれたマップファイルを全て読み込んで、そこにある全てのエントリについて、それぞれに適した書式に変換した上で、上述の出力対象マップファイルを更新する。*6
  • updmap --add foo.map を実行すると、updmap.cfg の末尾に「Map foo.map」を追記した後、上項の動作を行う。

updmap したいとき

既存のパッケージのインストールで updmap の使用が指定されている場合はそれに従うだけであるが、そうでない場合、例えば、古いフォントパッケージで説明文書で dvips での直接指定が述べられていた、あるいは自分で TFM を用意した等の場合でも、以下の条件を満たせば updmap を利用して複数のマップ設定を一度に済ませることが可能である。

  • 欧文フォントである、すなわち TFM が 8 ビット欧文のものである(Omega の ΩFM や和文 TFM でない)。
  • フォントが Type1 バイナリ形式(*.pfb)である。
  • マップの付加設定(リエンコード等)を含めて、dvips のマップのエントリとして表現できる。

この場合、dvips 用のマップファイルが用意できる(或いは既にされている)はずであるから、それをそのまま updmap のマップファイルとして用いる(つまり updmap --add引数に入れて実行する)だけでよい。逆に言うと、以上の条件を満たさない場合、例えば「和文 TFM である」「TrueType/OpenType 形式の実フォントである」という場合は updmap は使えず、各ソフトウェアについて個別に設定する必要があるということになる。*7

補足事項

  • W32TeX では ls-R を使う運用(mktexlsr 派)と使わない運用(deltexlsr 派)の両方が可能であるが、updmap どちらでも(正しく運用されている限りは)問題なく使用できる。
  • 上で述べた updmap が書き換え対象とするマップファイルについては、(updmap が絶対に使われないという前提が成立しない限りは)手動で書き換えてはいけない。*8
  • ちなみに、UnicodeTeX の場合でも、8 ビット TeX と同じ方法(TFM 経由)で定義されたフォントについては updmap で行った設定が有効である。具体的には、XeTeX は dvipdfmx の設定、LuaTeX は pdfTeX の設定を参照する。

*1:ついでに言うと、和文と欧文で別のマップファイルを使う必然性もない。仮に cid-x.map に欧文のマップを記述しても正常に動作する。

*2W32TeX の既定では $TMF(つまり $TEXMFMAIN)であるが、もし $TEXMFVAR が設定されている場合はそちらの TEXMF ツリーが使われる。このことは後掲の他の「書き換え対象のマップファイル」についても同様。

*3:つまり dvips -Pbi で起動した場合に参照される。W32TeX の dvips は -Pbi-Ppdf 等を指定しないとアウトラインでの埋込にならないことに注意。

*4:つまり、pdfTeX は既定では pdftex.map のみを読み込むと決められている。TeX 文書内で \pdfmapfile 命令を実行することで、マップファイルの変更や追加が可能になるのであるが、文書に関わりなく適用される「既定」を変えることはできないと思われる。

*5:だから TeX Live にはこれに相当するものはないはず。

*6:実は、実際の動作はもう少し複雑である。これについては後日説明したい。

*7:ただし、ptexlive の updmap では本家の TeX Live に対する拡張として、「和文 TFM + TrueType/OpenType」の組み合わせに対して dvips/dvipdfmx の一括設定を行うという機能がある。

*8:マップを書き換えたい時にどのファイルを使えばよいかについては後日改めて述べたい。

2011-09-19

updmap はなぜ動くのか (1)

BXattiritta パッケージについて、「普通に updmap を使えばよい」と書いた。恐らくは、これだけではほとんどの人は何をすればよいか解らないだろうし、設定手順を知っている人でも、そもそも updmap が何をするものなのかを理解している人はそう多くないだろう。ここでは BXattritta を題材にして、updmap の話をしようとする。ただし、例の資料を読んでいることを前提とする。

パッケージのファイル構成と配置場所

まずは、updmap を使うことが前提とされる場合の、パッケージのファイル構成および各ファイルの適切な置き場所について確認してみる。

BXattiritta の実質的な中身は以下の通りである。(METAFONT ソース *.mf は実際には全く使われないので無視する。)

  1. attr-r-{ot1,t1,ts1}.tfm
  2. attr-r-{ot1,t1,ts1}.pfb
  3. bxattritta.sty
  4. ts1attritta.fd
  5. bxattritta.map

3 は拡張子が .sty だから LaTeX のパッケージファイルである(\usepackage{bxattritta} で読み込まれる)。4 は以前の記事で触れられていた「フォント定義ファイル」である。*1つまり、この 2 つは LaTeX 用のファイルだから、TeX レベルでのフォント定義(DVI ウェアの設定)には直接関係しない。パッケージ名が BXattritta だから、

に置けばよいであろう。

TeX レベルでのフォント定義に相当するのは、例の資料によると TFM ファイルのはずである。ここでは 1 に挙げる 3 つがある。ファイル名*2から判断すると「例のフォント」を OT1、T1、TS1 の各エンコーディングで用いるためのものであろう。*3全く同じベース名の Type1 フォント(2 に挙げたもの)があるので、これらの TFM はそれぞれ別の実フォント(物理フォント)を参照していることが予想される。*4これらの置き場所は、例の資料によると*5

  • *.tfm → $LOCAL/fonts/tfm/public/BXattritta
  • *.pfb → $LOCAL/fonts/type1/public/BXattritta
ということになる。 残りの 5 の bxattritta.map であるが、拡張子が .map だから、「何らかの」マップファイルであることは解る。少し中を見てみよう。
attr-r-ot1  CMAttritta-Regular    <attr-r-ot1.pfb
attr-r-t1   CMAttrittaT1-Regular  <attr-r-t1.pfb
attr-r-ts1  CMAttrittaTS1-Regular <attr-r-ts1.pfb

updmap を使うという話なので、これは「updmap 用」のマップファイルということなのだが、実は updmap 用の形式は dvips 用の形式(のサブセット)になっている。残念ながら例の文書は dvips のフォントの取扱には直接触れていない*6が、そこで述べられているレンダラ―の 1 つの gsftopk は dvips 用の設定を借用している。すなわち、例の文書で「gsftopk 用」と言っているのが実は dvips 用でかつ updmap 用のマップファイルなのである。(付録 G の eggtoothcroc.map と上の内容を比べると何となく似ていることが解るだろう。*7)従って、例の資料に従うなら

  • *.map → $LOCAL/fonts/map/dvips/public

に置くことになろう。ただし、私自身は $LOCAL/fonts/map/dvips/BXattritta(末尾ディレクトリをパッケージ名にする)を推奨する。

これで全てのファイルの配置が完了した。ここでの重要事項をまとめる。

  • updmap でのフォントインストールで用いられるのは、Type1 フォント(*.pfb)と TFM ファイル(*.tfm)とマップファイル(*.map)である。
  • updmap 用のマップファイルは dvips 用(gsftopk 用)のマップファイルである。
  • 従って、配置場所は $LOCAL/fonts/map/dvips 以下になる。

もちろん、このままでは bxattritta.map はどのソフトウェアにも読み込まれないのでフォントインストールしたことにはなっていない。

updmap の実行手順

updmap の実行の手順そのものは非常に簡単である。W32TeX では以下のコマンドを実行するだけである。

updmap --add bxattritta.map

これで、dviout、dvips、dvipdfmx、pdftex の全てで attritta-r-ot1 等のフォントを使った TeX 文書が正常に出力されるようになる。このように、複数の DVI ウェアに対して Type1 フォントのマップの設定を一度に行うことこそが updmap の目的なのである。

UNIX 系の TeX ディストリビューション(その多くは TeX Live に基づく)では、典型的には updmap に「個人用」と「システム(全ユーザ)用」の 2 種類あり別のコマンドになっている。*8書式は次のようになる。

updmap --enable Map bxattritta.map
#(コマンド名「updmap」は正しいものに置き換えること)

ここからは、updmap がなぜ必要なのか、どうやって動いているのかの解説になる。この話の主眼は「updmap を実行するための方法」ではないので、構造が単純な W32TeX の updmap を念頭におくことにする。TeX Live 系の updmap は仕様自体は大きく異なるが、動作原理という点では W32TeX のものと変わらない。

なぜ updmap を使うのか

「updmap の恩恵」は、それがなかった時(実際に昔はなかった)の状況を想像すれば容易に理解できる。dvips、dvipdfmx、pdfTeX は Type1 フォントを直接扱える(PostscriptPDF 文書に埋め込める)が、それぞれ別のマップファイルを持っていて、また書式が異なる。*9dviout では、Type1 は gsftopk か ps2pk で PK フォントに変換することになるが、この 2 つもまた個別のマップファイルを持っている。*10ゆえに、全部のソフトウェアDVI ウェアとレンダラ)に対応しようとすると 1 つの設定をあちこちのマップファイルに記述する必要があり、大変な労力になることは明らかである。無論、あるユーザが常用するソフトウェアの数はそれほど多くないので、必ずしもそれだけの労力になるとは限らないが、実は、実際に「大量のマップファイル地獄」の被害を受ける人が確実に存在する。それはフォントパッケージの開発者(つまり「BXattritta パッケージ」とかを作っている人)である。折角パッケージを作るのだから、なるべく多くのソフトウェアに対応したいと思うのが開発者の常であり、また適当なマップファイルを書けばそれが可能であることは判っているのであるが、実際に対応しようとすると、大量のマップファイルを用意した上で、もっと大量の「インストール手順書」をドキュメントに書き連ねる必要に迫られるのである。*11

(続く)

*1:TS1/attritta の定義ファイルだけあって、T1 や OT1 のものがないのを不審に思うかも知れないが、これに相当するものは bxattritta.sty の中に書かれている。

*2:これらは「Berry 命名法」ではなく「ZR 命名法(謎)」に基づいている。

*3:「TS1 なんて初めて聞いた」という人は「texdoc encguide」をやってみよう。

*4:勿論それが正解なのだが、必然性はないことに注意。

*5:「例の資料」で示されているのは、TDS に従った配置、すなわち「TeX での常識的な配置方法」である。

*6:付録 I で dvips + Type1 フォントの設定例があるが、この文書があくまで dviout 用の「ビットマップPK フォント)に変換する」という方式を前提にしているため、dvips で通常行われる「アウトラインフォントを埋め込むための設定」にはなっていない。(つまりこの方法だと埋込がビットマップになってしまう。)

*7:ただしこのマップ指定は「再エンコード」を行わない、つまり「Type1 フォントが保持している既定のエンコーディングをそのまま用いる」という点が異なる。だから、「〜 ReEncodeFont」の指令や .enc ファイルの指定が存在しないのである。

*8:後者は updmap-sys というコマンド名で、管理者権限を要求されることが多い。なお、OS のパッケージ管理システムから入れた場合は、結局ファイルの配置などはシステム毎に大きく異なることになると思われる。

*9:Type1 の場合、pdfTeX は dvips と同じだが、dvipdfmx は異なる。

*10:例の文書で挙げられている「下位レンダラ」のうち、ttf2pk は Type1 には適用されない。hbf2gf は私も知らないので無視。gsftopk は dvips と設定を共有できる。ps2pk は別書式のマップファイルをもつ。

*11:実際に全てをサポートする必要に迫られる立場という点では、TeX ディストリビューションのメンテナも同じである。

2011-09-18

例の(マルベリでない)フォントの話 (2)

前回の続き)

この字形定義らしきものが何もない「ソース」だけだと何のことか解らないと思うので、もう少し詳しく説明しておく。

「コンクリートなフォントとか」で述べた通り、Computer Modern は METAFONT を用いた文字通り「メタなフォント」として設計されている。すなわち、Roman、Sans Serif、Typewriter の各々のウェイトやバリアント違いの書体が、全て「一つの字形定義のセット」のパラメタ(線の太さ、セリフの有無、アセンダ・ディセンダの大きさ、等)の値の違いとして実現されている。*1従って、適切なパラメタの値のセットを用意すれば、それだけで(つまり、「メタデザイン」は Knuth によるものをそのまま用いて)、特定の和文フォントのウェイトに合った「丸ゴシック」の形状の欧文フォントを作り出すことができるのである。*2前回示したものがソースであるというのはそういう意味なのである。

前回説明したように、METAFONT のソース(*.mf)で与えられたフォントは、DVI ウェア側で何の設定も行わなくとも、そのソースファイルさえ「見える」場所にあれば、TeX 処理系DVI ウェアで使える状態になる。これは METAFONT のソースがそれだけで TFMPK ファイルを生成するのに必要な情報をもっているからである。

大昔は、TeX で用いられるフォントというのは METAFONT で与えられるものに限られていた。しかし、現在では TeX 文書を作成する場合の最終目標物が「印刷された紙」ではなく「PDF 文書」に変わっていることが多く、その場合に埋め込んだフォントビットマップ形式であるのは甚だ都合が悪い。それゆえ、現在では元々 METAFONT ソースで与えられていたフォントも Type1 形式のアウトラインフォントに変換して用いることが多い。例えば、Knuth の CM フォントおよび AMS-LaTeX(つまり amsmath と amssymb パッケージ)で用いられる AMS フォントは現代的な TeX システムでは Type1 形式のものが用いられる。

というわけで、この「Attritta」フォントについても、Type1 形式に変換したものを LaTeX サポートと一緒にパッケージにして(ひっそりと)公開しておく。

一旦 Type1 形式に変換してしまえば、元が METAFONT だったということは全く関係がなくなるので、インストールの手順は普通に「Type1 フォント + TFM」で与えられたものについて updmap を利用して設定するということになる。

(それじゃ解らんという人はこちら


ちなみに、Knuth が用意した CM フォントの中には、「丸ゴシック(round sans-serif)」のフォントはないのかというと、実は存在する。「The TeXbook」の章末の引用句の出力で用いられている、「Computer Modern Sans Serif Quotation Style」というファミリで、TFM 名は直立体が cmssq8、斜体が cmssqi8 である。*3

f:id:zrbabbler:20110918194953p:image

よく見ると、このフォントは和文フォントの従属欧文に特徴的な、「ディセンダが小さい」という性質ももっている。attritta-r-ot1 のパラメタ値を決めるにあたって、このフォントでの値を参考にした。(他に cmr10、cmss10、cmssbx10 を参考にしている。)

*1:ただし「一つのセット」には例外もあって、例えば、イタリックの英小文字や数字は直立体のそれとは別の定義を用意している。

*2:さらに言うと、Computer Modern のソースでは、メインのソース(cmr10.mf 等)は「パラメタの値を設定して共通の下請けのコード(別のソースファイル)に処理を渡す」という構造になっているので、新しいフォントを作るときはこのメイン(パラメタの値のファイル)だけを用意すればよい。

*3:TeXbook での使用を用途としているので、デザインサイズが 8pt のもののみが存在する。このような特殊性があるためか、標準の LaTeX の NFSS ではこのファミリのサポートが除外されている。

2011-09-17

例の(マルベリでない)フォントの話 (1)

「pLaTeX でマルベリする件」で、「丸ゴシックフォントにあう欧文フォントが欲しい」という話をした後、次のような欧文フォントを紹介した。(和文は「マルベリ」です。)

f:id:zrbabbler:20110918032938p:image

このフォントの「ソースファイル」を掲載する。

[attr-r-ot1.mf]
% Attritta Regular
if unknown cmbase: input cmbase fi

font_identifier:="ATTRITTA"; font_size 10pt#;

u#:=20/36pt#;
width_adj#:=15/36pt#;
serif_fit#:=-7/36pt#;
cap_serif_fit#:=-10/36pt#;
letter_fit#:=0pt#;
body_height#:=304/36pt#;
asc_height#:=276/36pt#;
cap_height#:=276/36pt#;
fig_height#:=276/36pt#;
x_height#:=194/36pt#;
math_axis#:=108/36pt#;
bar_height#:=102/36pt#;
comma_depth#:=36/36pt#;
desc_depth#:=41/36pt#;
crisp#:=28/36pt#;
tiny#:=28/36pt#;
fine#:=12/36pt#;
thin_join#:=12/36pt#;
hair#:=34/36pt#;
stem#:=34/36pt#;
curve#:=34/36pt#;
ess#:=33/36pt#;
flare#:=31/36pt#;
dot_size#:=42/36pt#;
cap_hair#:=34/36pt#;
cap_stem#:=34/36pt#;
cap_curve#:=34/36pt#;
cap_ess#:=33/36pt#;
rule_thickness#:=28/36pt#;
dish#:=0pt#;
bracket#:=0pt#;
jut#:=0pt#;
cap_jut#:=0pt#;
beak_jut#:=0pt#;
beak#:=0/36pt#;
vair#:=27/36pt#;
notch_cut#:=34/36pt#;
bar#:=27/36pt#;
slab#:=28/36pt#;
cap_bar#:=27/36pt#;
cap_band#:=24/36pt#;
cap_notch_cut#:=34/36pt#;
serif_drop#:=0/36pt#;
stem_corr#:=0.5/36pt#;
vair_corr#:=1/36pt#;
apex_corr#:=9/36pt#;
o#:=8/36pt#;
apex_o#:=2/36pt#;
slant:=0;
fudge:=.91;
math_spread:=0;
superness:=1/sqrt2;
superpull:=1/10;
beak_darkness:=0;
ligs:=2;
square_dots:=false;
hefty:=true;
serifs:=false;
monospace:=false;
variant_g:=true;
low_asterisk:=false;
math_fitting:=false;

generate roman

この METAFONT のソースファイルをカレントディレクトリに置いた上で、例えば次のような、「attritta-r-ot1」というフォントTFM)を指定した TeX 文書(以下は plain TeX)を作成し、pdftex コマンド(または tex + dvipdfmx)で処理すると、上の画像と同じ欧文フォントで文字が出力されるであろう。*1

\font\attritta=attritta-r-ot1
\attritta Hamburgerfont
\bye

(続く)

*1:この場合、TFM ファイル attritta-r-ot1.tfmPK ファイル attritta-r-ot1.###pk(### は DPI値)がカレントに生成され、それが読み込まれている。dviout はカレントの PK ファイルを読めないので失敗する。$TEXMF/fonts/source/ 以下に .mf ファイルを置けば大丈夫である。

2011-09-11

LaTeX に新しいフォントを持ち込む話

前の記事であげた文書「欧文フォントのインストール」TeX レベルにおけるフォントの取扱を専ら扱っている。実際に LaTeX 上で新しいフォントを使いたいという人にとっては、LaTeX レベルでの設定――すなわち、新しいフォントLaTeXフォント命令で扱えるようにするための方法――の知識も必要である。幸い、例の文書の内容を習得した(つまり、TeX\font プリミティブでの定義ができる)人*1にとっては、LaTeX レベルの設定はそれほど難しくない。以下では、そういう人のために、必要最低限の知識を説明しようと思う。

LaTeX でのフォントの管理方法

まず、既存の(つまり「既に設定されている」)フォントを使う場合を例にとって、LaTeXTeXフォント管理方法の違いについて見ることにする。*2

例えば、T1 エンコーディングの「Times Bold Italic」を 12pt で使いたいとする。*3LaTeX の場合、まずその『フォント』の属するファミリ、シリーズ、シェープを考える: 「Times Bold Italic」はファミリ ptm のシリーズ b のシェープ it である。従って、エンコーディングとサイズを加えて、5 つの「属性」を指定することになる。*4

\fontencoding{T1}\fontfamily{ptm}\fontseries{b}\fontshape{it}%
\fontsize{12}{14}\selectfont Hello!

あるいは、サイズ以外の属性は \usefont 命令で一度に指定できるので、それを用いてもよい。*5

\usefont{T1}{ptm}{b}{it}\fontsize{12}{14}\selectfont Hello!

さらに「文書の既定のフォントを T1 の Times にする」という前提であれば、以下のようにするのが一般的であろう。*6

% プレアンブルで
\usepackage[T1]{fontenc}
\renewcommand{\rmdefault}{ptm} % \rmfamily を ptm にする
% 当該の箇所で
\rmfamily\bfseries\itshape\large Hello!

使う側の書き方は色々あるが、いずれにしても、LaTeX ではこのように複数の属性を用いてフォントを管理している(このフォント管理の仕組みを NFSS(Nww Font Selection Scheme)と呼ぶ)。

TeX でのフォントの管理方法

それでは、TeX のプリミティブでのレベルで「T1 エンコーディングの Times Bold Italic の 12pt」を指定するにはどうすればよいか。まず当該フォントTFM 名を求める。「T1 エンコーディングの Times Bold Italic」の TFM 名は ptmbi8t である。*7従って、以下のような指定方法になる。

% 予め定義しておく
\font\TITimesBoldItalicXII=ptmbi8t at 12pt
% 当該の箇所で
\TITimesBoldItalicXII Hello!

つまり、上のようにして \font で定義された \TITimesBoldItalicXII という制御綴が TeX でのフォントの表現(「fontdef トークン」と呼ばれる)となっている。これで判るように、TeX では、「属性」がどれか違うものは全て「全く違うフォント(=fontdef トークン)」として取り扱われる。

\font\TITimesBoldItalicXII=ptmbi8t at 12pt    % T1, Times Bold Italic 12pt
\font\LYITimesBoldItalicXII=ptmbi7y at 12pt   % LY1, Times Bold Italic 12pt
\font\TIPalatinoBoldItalicXII=pplbi8t at 12pt % T1, Palatino Bold Italic 12pt
\font\TITimesItalicXII=ptmri8t at 12pt        % T1, Times Italic 12pt
\font\TITimesBoldXII=ptmb8t at 12pt           % T1, Times Bold 12pt
\font\TITimesBoldItalicX=ptmbi8t at 10pt      % T1, Times Bold Italic 10pt

この扱いは、「TFM レベル」での扱いとも異なっていることが、上掲の最初と最後の定義を比べると解る。TFM では「フォントサイズのみが異なる」ものは同じフォント(ptmbi8t)として扱える。*8TeX では「TFM とサイズ」を組にしたものを「1 つのフォント」(つまり、\TITimesBoldItalicXII\TITimesBoldItalicX)としているのである。

NFSS は何をしているのか

以上のことを考え合わせると、「LaTeXフォント管理システム(NFSS)が何をしているのか」が判る。つまり、要件としては、LaTeX の「エンコーディング T1、ファミリ ptm、シリーズ b、シェープ it、サイズ 12pt」*9という指定から「fontdef トークン」を作り出す必要がある。一方、TeX\font 命令で fontdef トークンを定義するには TFM とサイズが必要である。サイズは指定されたものをそのまま使えばよい。従って、NFSS が行うべきことは

T1/ptm/b/it ―→ ptmbi8t

のように、「サイズ以外の 4 つの属性値」から「TFM 名」への写像ということになる。

実際に新しいフォントを導入してみよう

原理の概要が判ったところで、実際に新しいフォント(つまり、TFM として定義されたもの)を LaTeX に導入する設定例を述べる。ただし、ここでは最も単純な場合として、「単一のフォントからなるファミリ」を扱うことにする。例としては、件の文書の 5.3 節においてインストール例として挙げられている「Plasma Drip BRK」(TFM 名 plasma)を用いる――つまり、その節の手順に従って、TeX レベルのインストール作業は既に済んでいる状態を前提とする。*10

TFM は既に与えられいる(plasma.tfm)ので、それの対応元となる 4 つの属性値(サイズ以外)を決める必要がある。

?/?/?/? ―→ plasma

このうち、エンコーディングは既に決まっていて、例の文書の通りに作業したのであれば、それは LY1 のはずである(texnansi.enc を使っているので)。ファミリ名については、これから新たに導入するのだから好きなように命名すればよい。ここでは単純に plasma としよう。「フォント」が 1 つしかないので、この plasma ファミリには plasma.tfm のみで構成される。実は、シリーズとシェープの値も何でも良い(実際に使うときに \fontseries 等に「設定した名前」を指定すれば通用する)のであるが、ここではメンバーが 1 つしかないので、「最も普通」を意味するシリーズ m (= medium)とシェープ n (= normal)を当てることにしよう。これで「設定すべき内容」が確定した。

LY1/plasma/m/n ―→ plasma

それでは、実際に設定作業に取り掛かる。上のような属性値から TFM への対応は「フォント定義ファイル」に記述するのが標準的な方法である。*11フォント定義ファイルはファミリ毎に用意し、ファイル名は「(エンコーディング名の小文字)(ファミリ名).fd」とする。このファイルにまず「新しいファミリを定義する命令」である \DeclareFontFamily 命令を記述する。今の場合、次のようになる。

[ly1plasma.fd]
% LY1/plasma のフォント定義ファイル
  % \DeclareFontFamily{エンコーディング}{ファミリ}{}
  % 第 3 引数は普通は空で構わない
\DeclareFontFamily{LY1}{plasma}{}

この後に、「属性値から TFM への対応」を \DeclareFontShape 命令を用いて記述する。今の場合、対応(TFM)が 1 つしかないので、この命令の記述も 1 つになる。

  % \DeclareFontShape{エンコーディング}{ファミリ}{シリーズ}{シェープ}
  %   {<->TFM名}{} : 最後の引数は普通は空で構わない
\DeclareFontShape{LY1}{plasma}{m}{n}{<->plasma}{}

TFM 名の前にある「<->」については、「オプティカルサイズ」でない場合は常にこれを付けると思って構わない。*12これでフォント定義ファイルは完成である。この ly1plasma.fd ファイルを次の場所に配置する。*13

  • $LOCAL/tex/latex/plasma/ly1plasma.fd
    (「パッケージ名」を plasma とした。)

これで設定は全て完了である。この後で、任意の LaTeX 文書中で「LY1/plasma/m/n」の指定を行う(例えば \usefont{LY1}{plasma}{m}{n} を実行する)と、先の定義ファイルが読み込まれて、現在のフォントサイズの plasma.tfmフォントが変更される。ここで、「1 つのシェープしかないのだから、もっと単純に、例えば \plasmafont とかで使えるようにしたい」というのであれば、それは単なる「LaTeXマクロ定義」*14の問題である。

実際に LaTeX 文書でフォントを使ってみる

[test-plasma.tex]
\documentclass{article}
  % LY1 エンコーディングを使用可能にするが、既定は OT1 のまま
\usepackage[LY1,OT1]{fontenc}
  % 素敵な色をつけましょう
\usepackage{color}
\definecolor{dred}{rgb}{0.8,0,0}
\definecolor{dgray}{rgb}{0.1,0.1,0}
\pagecolor{dgray} % ページ背景色の設定
\begin{document}
\begin{center}
  % LY1/plasma/m/n(つまり plasma.tfm)の 50pt を指定
  \usefont{LY1}{plasma}{m}{n}\fontsize{50}{50}\selectfont
  \color{dred}Happy {\TeX}ing!
\end{center}
\end{document}
f:id:zrbabbler:20110911193826p:image

*1:当然ながら、「LaTeXフォント関連コマンド(\fontshape 等)について知っていることも前提となる」

*2:なお、この記事では、「オプティカルサイズ」(サイズによりデザインの異なる)の取扱については、実際にそのようなフォントを導入することが稀であるという理由で割愛することにする。

*3:Times などの「Adobe 基本書体」は LaTeX で最初から適切な設定が行われているので、それを用いることを前提とする。

*4\fontsize の第 2 引数は行送り(この場合 14pt)を表す。これはフォントの属性ではないが、LaTeX ではフォントサイズは行送りと共に指定することになっている。ちなみに、article クラスの \large はこの \fontsize{12}{14} と同じである。

*5\usefont は中で自動的に \selectfont を行うので、それのみ使うのなら \selectfont は不要。

*6\large = \fontsize{12}{14} とする。なお、\bfseries は本当は \fontseries{bx} であるが、ptm ファミリで bx シリーズが指定された場合は黙って b シリーズに代替される。

*7:なお、この名前は一定の規則(Berry 命名規則)に基づいて決められたものだが、この規則は単なる習慣であって、TeX 自体はこの規則とは無関係であることに注意されたい。Berry 命名規則についてはいつか話をしたい。

*8:Computer Modern はサイズにより TFM が異なる(例えば、cmt10、cmr9、cmr17、等)が、これの理由は「オプティカルサイズ」だからである。(つまり「サイズだけが異なる」のではない。)

*9:便宜的に、この属性値の組をまとめて「T1/ptm/b/it/12」のように表記することが多い。

*10:私は DailyFreeFonts.com からこのフォントファイルを入手した。

*11:このようにしておくと、\usepackage 等で設定ファイルを明示的に読み込まなくとも、該当のファミリが呼び出された時点でそのファイルが自動的に読み込まれる。

*12:「オプティカルサイズ」である場合にこの部分がどうなるかは、OT1/cmr のフォント定義ファイル、すなわち ot1cmr.fd を見れば判るだろう。

*13:例の文書の表記方法に従う。勿論、この後に「必要ならば mktexlsr を実行する」の但し書きがつく。

*14:「TeXマクロ」ではないですよ(念のため)。

2011-09-10

「欧文フォントのインストール」が素晴らしい件について

TeXdvioutフォントが表示されるからくりを実験しながら調べてみました。
PDFにしたら60ページになりました。eggtoothcrocの日記 「欧文フォントのインストール」

感動した!



こういう「TeXシステムでのフォントの扱い」の詳細は語られることが少ないのであるが、この文書を見ると、その理由が判ると思う。要するに、あまりに複雑で、「何も知らない人」に対して通用するような短い説明が極めて困難だからである。フォントというのは定型処理がなかなか通用しない曲者で、Web 上に散在する「ノウハウ」も実際には失敗することが多い。こういう時に「中身の理解」を避けてしまうと、自分で解決手段を探ることも他人の助けを求めることもできずに完全な手詰まりに陥ってしまう。それ故、「中身の解説」が手の届くところにあるのは非常に有り難いことである。

2011-09-03

TeX での末尾再帰 (6)

プログラミング言語において、関数(あるいはメソッド)に与える引数の数は(静的型付け言語の場合は型も)通常は関数ごとに固定されていてその関数の呼出において変わることはない。しかし例外的に、引数の個数(や型)が呼出毎に変わりうるような関数があり、そのような関数可変長引数をもつと呼ばれる。C 言語(や他の多くの言語)の printf() は可変長引数もつ関数の代表例である。

printf("%s\n%d\n", "参照先が見つからない", 7);

呼び出された関数の側で可変長引数をどう扱うかについては言語毎に方法に大きな違いがある。RubyJava では可変長引数引数列が自動的に配列に変換される。

Ruby の可変長引数の取扱の例)
def func_with_vararg(first, *rest) # 可変部分に * を付した引数名を書く
  p first     # first の値を表示
  p rest      # rest の値を表示
end
func_with_vararg(1, 2, 3, 4)
# (実行結果)
# 1
# [2, 3, 4]        ←rest は配列になっている

JavaScriptPerl の場合は、「与えられた引数全てからなる配列」が常に用意されているので、それを利用して可変長引数を利用できる。*1

JavaScript の可変長引数の取扱の例)
function func_with_vararg(first) { // 可変部分は書かない
  // arguments は「全ての」引数を保持している
  print(first); // arguments[0] でも同じ
  for (var i = 1; i < arguments.length; i++) // 可変部分
    print(arguments[i]);
}
func_with_vararg(1, 2, 3, 4);
// (実行結果: 以下の引数で print が順に呼ばれる)
// 1
// 2
// 3
// 4

いずれの方法にしても、不定の個数をもつ引数を格納する配列(リスト)を用いることになる――不定複数の値を保持するのだから、配列を使うのは必然であろう。ところが、Postscript 等の「スタック型言語」*2の場合は様子が異なる。そのような言語では、関数呼出の際に引数スタックオペランドスタック)を通して渡される。スタックは元々不定数のデータを保持できるので、可変引数のために配列を用いる必要がないのである。

スタック型言語での可変長引数の処理はどうすればよいか。ループの中でスタックから 1 つずつ引数を取り出す(ポップする)ことになるが、引数の個数が不明なので、このままではどこまで取り出せばよいかが判らない。*3そこで、通常のデータとしては使われない「特別な値」を用意する方法が考えられる。すなわち、呼び出す側では引数列をプッシュする前に「特別な値」をプッシュしておく(つまり、スタック中では引数の値の下にそれが置かれる)。呼び出される側は、ループの中で 1 つずつ値をポップし、それが「特別な値」であれば取り出しを終了すればよい。以下の例は Postscript で可変長引数として渡された複数の数値の和を返す関数である。Postscript では「特別な値」として使うために用意された「マーク」という値があり、mark 演算子はこの「マーク」をプッシュする。*4

% mark num1 ... numn  sumall  sum
/sumall {
  1 dict begin
    /sumresult 0 def
    {
      dup mark eq { pop exit } if
      sumresult add /sumresult exch def
    } loop
    sumresult
  end
} def
% 実行例: 100 - (1 + 2 + 3 + 4) の値を表示
100 mark 1 2 3 4 sumall sub =  %=> 90

……おっと、TeX の話をしなければ。

実は、TeX での可変長引数の扱いは、先述のスタック型言語でのそれと似ている部分がある。これを見るために、あるマクロがまさに展開されようとする時点を考える。

\macroA{4}{3}{2}{1}……

ここで、\macroA の後に続く「引数」の列({4}{3}…)をスタックであると考える。*5前側がスタックの上側(トップ側)に相当する(つまりトップにある値は 4)。\macroA が 1 つの引数を取る(つまり \def\macroA#1{...} のように定義された)とすると、それを展開することで、トップにある 4 が「ポップされる」ことになる。さらに、その後に展開・実行が進んで、\macroA{4} の部分が \macroA の末尾再帰に置き換わったとすると、このマクロは 1 度に 1 つずつ引数を「スタックからポップ」しながら繰り返し処理を行うということができる。Postscript の場合と同じく、このままではどの引数までが処理対象であるかが判らないことになるが、ここでも「特別な値」を使って当該のマクロの可変長引数の終端を示すことができる。

% \sumall{{num_1}{num_2}...{num_n}} :
%   num_1 + num_2 + ... + num_n を \countResult に代入する
\newcount\countResult
\def\sumall#1{%               % {{3}{2}{1}} を
  \xx@sumall@a#1\xx@end       % {3}{2}{1}\xx@end の形に変換
}
\def\xx@end{\xx@end@}         % ユニークトークン
\def\xx@sumall@a{%
  \countResult=0
  \xx@sumall@b
}
\def\xx@sumall@b#1{% 再帰によるループ
  \ifx#1\xx@end % 何もせず終了
  \else
    \advance\countResult#1\relax
    \expandafter\xx@sumall@b % \expandafter で末尾再帰を保証
  \fi
}
% 実行例
\sumall{{4}{3}{2}{1}}
\immediate\write16{Answer = \the\countResult}
% --> Answer = 10

TeX配列型のない言語であり、また可変長引数に関する支援もない。従って、現実のプログラミングで可変長引数を扱う場合には専らこの「引数をポップする末尾再帰」が使われているのである。いくつか注意をしておく。

  • この手法を使う場合、正しい末尾再帰の形にしておかないと、(資源を消費するという話の前に)再帰呼出を行った時の引数が正しくならず全く正常に機能しないことになる。例えば、上のコードで \xx@sumall@b\expandafter が抜けていると再帰呼出が \xx@sumall@b\fi{3}... となり失敗する。
  • 上のようにマクロ \xx@end を定義すると、「\xx@end@ という制御綴を他の箇所では一切使用しない」という条件のもとで、\xx@end\ifx の比較において自身とのみ等価と比較されるトークンとなる。単に \xx@end を未定義にしておくと、\ifx では他の任意の未定義の制御綴と等価と判断されるので注意。

*1Perl では引数@_ という名のリスト変数に参照渡しされる。他の多くの言語と異なり個々の引数変数を割り当てる書き方がない(Perl6 は違うようだが……)。多くの場合、関数の先頭で my ($x, $y) = @_; のようにローカル変数に値をコピーする。ここで、my ($first, @rest) = @_; のように「可変部分をリストにする」こともできる。

*2:ところで、スタック型言語で有名なものはないのかな? 私が思いついたのは Postscript の他には Whitespace と BibTeX しかないのだが。スタック機械のアーキテクチャは現在でもよく用いられているとは対照的にスタック型言語のプレゼンスは限定的なのかな。

*3スタックに「その関数引数でないもの」が残っているかも知れないので全部取り出してしまうのは不適切である。

*4:ただし、Postscript には「スタック中のマークより上にある値の個数」を調べる counttomark という演算子があり、実際のプログラミングでは、これを用いて個数を調べた上でその回数だけ引数のポップを行うという処理を用いることが多いであろう。

*5:簡単のため、区切り無し引数のみを用いると仮定する。

2011-09-02

画期的なソートアルゴリズム: TeXsort

一昨日の記事で出題した問題のうち、一番難しいと思うのは最後のリストのソートである。ソート(整列)は実際の LaTeX パッケージの開発においても必要性のある処理だと思われる。*1しかし、バブルソート等の非常に単純なアルゴリズムでも、実行・展開の制御を身につけていないと TeX で実装するのは極めて難しい。

だがしかし!

諸君!オレは天才かもしれない。このソートアルゴリズムをみてくれ。こいつをどう思う?

\documentclass{article}
\makeatletter
\def\TeXsort#1{%
  \leavevmode \@tempdima=2em
  \@for\xx@x:=#1\do{\rlap{\kern\xx@x\@tempdima\xx@x}}}
\makeatother
\begin{document}
\TeXsort{7,12,2,6,3,11,5,9}
\end{document}

f:id:zrbabbler:20110901094153p:image

なん…だと…。こいつ、動くぞ!

っておい!リストに 50 とか 100 とかがあると紙面からはみ出してしまうのかよ!

(訳文の元ネタ:http://d.hatena.ne.jp/gfx/20110519/1305810786

*1:少なくとも私は、TeX 上のプログラミングで二分木を使おうと思ったことは一度もない。