アウトラインのSVGからフォントを生成 #かな書いてみる
IVSやら何やら他の話題に飛びついていたので間が空いてしまったが、明朝かな書体制作のつづき。前回はアウトラインを作ったので、今回はここからフォントを生成したい。
SVGの分割
まず、これがアウトラインのファイル。Inkscape で作成し、SVGとして保存した。アウトラインは outline という名前のレイヤーの中に作っている。仮想ボディのサイズは100pxとした。
これをグリフごとに分割し、1文字1ファイルにする。今回は(も?)Perl で簡単なスクリプトを書き、これを利用した。単純なものなので、path 要素以外の要素(グループも含む)に対応していないなどいろいろと制限はある。また XML::Simple を利用しているので、実行するには XML::Simple のインストールが必要。
#!/usr/bin/perl # # SVGをグリフごとに分割 # usage: perl split_svg.pl svg_sheet.svg list.txt use strict; use warnings; use autodie; use utf8; use 5.010; binmode STDOUT, ":utf8"; use constant OUT_DIR => "glyphs"; use constant PIXELS_PER_EM => 100; use constant UNITS_PER_EM => 1000; use XML::Simple; my ($svg_file, $list_file) = @ARGV; # SVG 読み込み my $svg = XMLin($svg_file, forcearray => 1, keyattr => []); # 行数・列数を求める my $row_max = int ($svg->{height} / PIXELS_PER_EM) - 1; my $col_max = int ($svg->{width} / PIXELS_PER_EM) - 1; # outline レイヤーを探す my $groups = $svg->{g}; my $group_outline; foreach my $group (@$groups) { if ($group->{'inkscape:label'} eq "outline") { $group_outline = $group; last; } } die "outlineレイヤーが存在しません" unless defined $group_outline; # 変換行列を求める my $transform = $group_outline->{transform}; my ($a, $b, $c, $d, $e, $f) = (1, 0, 0, 1, 0, 0); if (!defined $transform) { # noop } elsif ($transform =~ m/translate\((?<tx>-?\d+(.\d+)?(e-?\d+)?),(?<ty>-?\d+(.\d+)?(e-?\d+)?)\)/) { $e = $+{tx}; $f = $+{ty}; } elsif ($transform =~ m/matrix\((?<a>-?\d+(.\d+)?(e-?\d+)?),(?<b>-?\d+(.\d+)?(e-?\d+)?)\),(?<c>-?\d+(.\d+)?(e-?\d+)?),(?<d>-?\d+(.\d+)?(e-?\d+)?),(?<e>-?\d+(.\d+)?(e-?\d+)?),(?<f>-?\d+(.\d+)?(e-?\d+)?)/) { $a = $+{a}; $c = $+{c}; $e = $+{e}; $b = $+{b}; $d = $+{d}; $f = $+{f}; } else { die "未対応のtransformです: $transform" } # グリフごとに path を格納する変数 my @glyphs = (); foreach my $row (0 .. $row_max) { foreach my $col (0 .. $col_max) { $glyphs[$row][$col] = []; } } # @glyphs にパスを格納 my $paths = $group_outline->{path}; foreach my $path (@$paths) { my @data_args = split /\s+/, $path->{d}; my $new_data = ""; my ($col, $row); my $is_moveto_point = 1; foreach my $data_arg (@data_args) { if ($data_arg =~ m/(?<x>-?\d+(.\d+)?),(?<y>-?\d+(.\d+)?)/) { my $x = $+{x}; my $y = $+{y}; if ($is_moveto_point) { $x = $a * $x + $c * $y + $e; $y = $b * $x + $d * $y + $f; $col = int ($x / PIXELS_PER_EM); $row = int ($y / PIXELS_PER_EM); $x %= PIXELS_PER_EM; $y %= PIXELS_PER_EM; $is_moveto_point = 0; } $x *= UNITS_PER_EM / PIXELS_PER_EM; $y *= UNITS_PER_EM / PIXELS_PER_EM; $data_arg = "$x,$y" } $new_data .= "$data_arg "; } next if ($col < 0 || $col_max < $col || $row < 0 || $row_max < $row); $path->{d} = $new_data; push @{$glyphs[$row][$col]}, $path; } # リスト読み込み my @glyphname_list = (); open my $fh_list, '<:utf8', $list_file; while (my $line = <$fh_list>) { chomp $line; my @list = map { sprintf "u%x", (unpack "U*", $_) } split / +/, $line; push @glyphname_list, \@list; } close $fh_list; # 各グリフの SVG を生成 mkdir OUT_DIR if !-d OUT_DIR; foreach my $row (0 .. $row_max) { foreach my $col (0 .. $col_max) { my $svg; $svg->{'xmlns:sodipodi'} = "http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"; $svg->{'xmlns:inkscape'} = "http://www.inkscape.org/namespaces/inkscape"; $svg->{width} = UNITS_PER_EM; $svg->{height} = UNITS_PER_EM; $svg->{path} = $glyphs[$row][$col]; my $svg_string = '<?xml version="1.0" encoding="UTF-8"?>'; $svg_string .= "\n" . XMLout($svg, RootName => "svg"); my $glyphname = $glyphname_list[$row][$col]; next if (!defined $glyphname || $glyphname eq "u3000"); # u3000:全角スペース open my $fh_out, '>', OUT_DIR . "/$glyphname.svg"; print $fh_out $svg_string; close $fh_out; } } exit 0;
SVGのどのマスがどのグリフか、割り当て表も用意しておく。
あ い う え お か き く け こ さ し す せ そ た ち つ て と な に ぬ ね の は ひ ふ へ ほ ま み む め も や ゆ よ ら り る れ ろ わ ゐ ん ゑ を
これを例えば
> perl split_svg.pl hiragana_1.svg hiragana_list.txt
として実行すれば、glyphs ディレクトリに一字一字のSVGが生成される。
FontForge でフォント化
そして、このバラしたSVGファイルを FontForge に取り込み、フォントを生成する。
FontForge ではSVGの1pxがフォントの1ユニットに相当する。また、OpenType の標準は1000ユニット/emなので、インポート元のSVGを1000×1000pxにしておくとちょうど良い。今回は、split_svg.pl でバラすときにSVGのサイズを1000×1000pxへ変換しているので、あとはそのままインポートするだけでよい。
#!/usr/bin/fontforge -script if ($argc != 2) Print("usage: fontforge -script " + $0 + " [version]") Quit() endif _version = $1 _fontfilename = "zeromin_" + _version + ".otf" _importfiles = "glyphs/u*.svg" New() # .notdef作成 Select(0x0000) SetWidth(1000) SetGlyphName(".notdef") # エンコードにUnicodeを指定 Reencode("unicode") # SVGをすべてインポート Import(_importfiles, 0) # 自動ヒントづけOFF SelectAll() DontAutoHint() # パスの統合 RemoveOverlap() # 整数値に丸める RoundToInt() # 半角スペース作成 Select(0u0020) SetWidth(500) # 全角スペース作成 Select(0u3000) SetWidth(1000) # フォント情報設定 SetFontNames("ZeroMin",\ "ZeroMin",\ "ZeroMin",\ "Regular",\ "© 2012 mashabow",\ _version) SetOS2Value("WinAscent", 880) SetOS2Value("WinDescent", 120) SetOS2Value("HHeadAscent", 880) SetOS2Value("HHeadDescent", -120) # OTF生成 Generate(_fontfilename) Print("generated: "+ _fontfilename) Close() Quit()
このスクリプトを FontForge で動かせば*1、とりあえずはフォントが生成される。最初の明朝体ということで「ZeroMin」と仮に名付けてみた。
試し打ち
出来上がったフォントをインストールして、さっそく試し打ち。
んー。初めてにしてはまずまずなような気もしないでもないけど、やっぱり字面の大きさや寄り引き、太さなんかのバラつきが目立つ。実用にはちょっと堪えない。
というわけで、まだまだ先は長そう。