# TrueTypeラスタライズ
PDFにフォントを埋め込むために必須ではないが、TrueTypeフォントをベクトルデータに変換しSVG形式に変換する方法を解説する。
* [mdn SVG](https://developer.mozilla.org/en-US/docs/Web/SVG){:target="_blank"}
* [Wikipedia SVG](https://en.wikipedia.org/wiki/SVG){:target="_blank"}
## glyfテーブルデータ
TrueTypeのglyfテーブルには[Simple glyph](https://learn.microsoft.com/ja-jp/typography/opentype/spec/glyf#simple-glyph-description){:target="_blank"}と[Composite glyph](https://learn.microsoft.com/ja-jp/typography/opentype/spec/glyf#composite-glyph-description){:target="_blank"}の2種類がある。
Simple glyphがグリフの座標データを持つもので、Composite glyphは複数のグリフを組み合わせて1つの文字を表現するものである。
Simple glyphであるかどうかはヘッダーの[numberOfContoursが-1](https://learn.microsoft.com/ja-jp/typography/opentype/spec/glyf#glyph-headers){:target="_blank"}であるかどうかで判断する。
## Simple glyph
Simple glyphはflags、xCoordinates、yCoordinatesの3組の配列で表現される。
numberOfContoursが組の数となる。
xCoordinates、yCoordinatesは直前の座標からの相対座標になる。
絶対座標に変換する場合は事前に変換しておくとよい。
座標変換は次のような畳み込みで解決できる。
```cs
int[] to_absolute(int[] xs) => xs[1..].Aggregate([xs[0]], (ys, a) => [.. ys, ys[^1] + a]);
```
endPtsOfContoursから組を取得して面を構成する。
面を構成するパスは閉じている必要があるため、最後の点から最初の点につなぐ必要がある。
SVGであれば[パスを閉じるコマンド](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/d#closepath){:target="_blank"}がある。
```cs
var xs = to_absolute(xCoordinates);
var ys = to_absolute(yCoordinates);
var prev = 0;
for(var i = 0; i < numberOfContours; i++)
{
var vs = xs[prev .. endPtsOfContours[i]]
.Zip(ys[prev .. endPtsOfContours[i]])
.Select(a => new Vector2(a.First, a.Second));
if (vs.First() != vs.Last()) vs = [.. vs, vs.First()]; // 必要に応じて追加
prev = endPtsOfContours[i] + 1;
}
```
グリフは直線と[2次ベジェ曲線](https://en.wikipedia.org/wiki/B%C3%A9zier_curve){:target="_blank"}で表現される。
直線か曲線かの判断はflagsのON_CURVE_POINT(線上の点)で判断する。
始点と終点が両方ON_CURVE_POINTであれば直線である。
終点がON_CURVE_POINTでない場合は2次ベジェ曲線の制御点となる。
制御点が2回連続して出現することがある。この場合は制御点の中間点を終点とした曲線にする必要がある。
下図は青点がON_CURVE_POINT、赤点が制御点、緑点が中間点である。
文字の下側に引かれた赤線はベースラインである。
yCoordinatesの座標はベースラインより上がプラス、下がマイナスとなる。(数学の座標と同じ、x軸がベースライン)
日本語グリフではベースラインより下側を使用することは少ないが、アルファベットのjなどはベースラインより下側に描画する。
xCoordinatesは通常はプラスであることが多いが、マイナスにもなりうる。
## Composite glyph
Composite glyphは複数のグリフのGIDを指定する。
指定したGIDもComposite glyphである可能性がある。循環してはいけない。
各グリフを移動、回転、拡大縮小して組み合わせる。
argument1、argument2で移動を表す。
フラグのうちWE_HAVE_A_SCALE、WE_HAVE_AN_X_AND_Y_SCALE、WE_HAVE_A_TWO_BY_TWOは排他フラグとなる。
アフィン変換行列を作って座標移動するとよい。
```cs
var m = Matrix3x2.Identity;
m.Translation = new Vector2(aArgument1, argument2);
if (flags.HasFlag(WE_HAVE_A_SCALE))
{
m.M11 = m.M22 = scale;
}
else if (flags.HasFlag(WE_HAVE_AN_X_AND_Y_SCALE))
{
m.M11 = xscale;
m.M22 = yscale;
}
else if (flags.HasFlag(WE_HAVE_A_TWO_BY_TWO))
{
m.M11 = xscale;
m.M12 = scale01;
m.M21 = scale10;
m.M22 = yscale;
}
var vec = Vector2.Transform(v, m); // 各種座標vをアフィン変換
```
scale、xscale、yscale、scale01、scale10は[F2DOT14](https://learn.microsoft.com/ja-jp/typography/opentype/spec/otff#data-types){:target="_blank"}である。
F2DOT14は上位2bitが整数部、下位14bitが小数部の固定小数点形式であるため、floatへ変換する場合は2 ^ 14(=16384)で割る必要がある。