# 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)で割る必要がある。