WindowsOSと.NET Frameworkで外字を表示する最終解(と思われるもの)


アプリケーションで外字を表示するためにEUDCをFontFamilyに指定したのに、表示されないとか、ファイル名やメモ帳とは違う文字が出るとか、そういう事ありませんでしたか？
自分それに遭遇しました。原因調べて対応方法を見つけたので書いておきます。
ネットには具体的にこうだというまとまった情報がなかったので。
Windows7で確認した内容です。ほぼ確実にVistaも変わらない。
XPやWindows8やWindowsRTやWindows Phoneでも同様なのかは調査してないですが、まぁ似たようなものかと思われます。

##外字ファイルの適用方法

外字の適用って外字エディタ使う場合あるけど、それは今回の話とはあまり関係ないので除外して考える。

外字ファイルというのはEUDC.TTEとかEUDC.EUFとかの事。
TTEはフォントが、EUFはフォントの管理情報が入ってます。
これをコピーしたり、ツールを仕様したりして外字を登録すると思います。

しかし、外字の登録にはおおまかに分けて以下の2パターンがあるのです。

###フォントフォルダへのインストール

外字ファイルをエクスプローラで開いたC:¥Windows¥Fonts¥の場所にドラッグアンドドロップしたり、C:¥Windows¥Fonts¥にバッチとかコマンドで直接コピーしたりしてフォントのインストールをする手法。
これが一般的(だと思っていた)。

###レジストリを弄ってのインストール

外字ファイルを任意の場所に置く。
で、レジストリエディタなり.regファイルなりを使ってとある場所のレジストリ値を変更すると外字が適用される。(外字エディタを起動するなり再起動するなりする必要あり)
変更する場所は`HKEY_CURRENT_USER\EUDC\932`の`SystemDefaultEUDCFont`。
このレジストリに`<任意の場所>\EUDC.TTE`って値を設定すればいい。`C:¥Gaiji¥EUDC.TTF`とか。

この適用方法から今回の問題が発生する。
ちなみに外字の移行とかで有名だと思う[外字コピー屋さん](http://www.eastvalley.or.jp/cpeudc/)もこの手法使ってる。(ver.3.0β2で確認)

##.Net Frameworkの挙動

.Net Frmeworkで外字を表示する場合、大体FontFamilyに以下のような設定をするでしょう。
多分まぁXAMLでこういう風に。

```XML:外字の適用方法
<!-- こうとか -->
<TextBlock FontFamily="メイリオ, file:///C:/Windows/Fonts/#EUDC">〜略〜</TextBlock>

<!-- スタイルとか -->
<Style TargetType="{x:Type TextBlock}">
	<Setter Property="FontFamily" Value="メイリオ, file:///C:/Windows/Fonts/#EUDC" />
</Style>
```

.Net Frmework3.5だと`file:///C:/Windows/Fonts/#EUDC`じゃなくて`EUDC`でも行ける。
4.0以降では`EUDC`だけだと表示されなくなってる。`EUDC`だけ指定した時の挙動がクソ怪しいのでぶった斬ったんだと思う。
3.5で`EUDC`指定した時は大抵の場合`file:///C:/Windows/Fonts/#EUDC`を指定したのと同じ挙動になると思っていい。

ここまで来たらもう分かりますよね。
レジストリの変更で外字をインストールすると、`C:/Windows/Fonts/`に外字フォントなんてないか、違う外字ファイルあるから外字が表示されなかったり違う文字になったりするんですよ。

みんなで一緒に〜　これから一緒に〜　レジストリで外字を適用している奴らを殴りに行こうか(真顔)

##導かれる最終解

上記までの事情から導かれる答えはひとつ。
**プログラムの起動時にレジストリの値を見に行って外字フォントの場所をフルパスで特定してやればいい。**
めんどくせぇ……

フォント指定したいXAMLファイルで以下の様に書いておく

```XML:フォントの指定例
〜〜〜

<Window.Resources>
	<FontFamily x:Key="MyWindowFontFamily">メイリオ, file:///C:/Windows/Fonts/#EUDC</FontFamily>
</Window.Resources>

〜〜〜

<!-- こうとか -->
<TextBlock FontFamily="{DynamicResource MyWindowFontFamily}">〜略〜</TextBlock>

<!-- スタイルとか -->
<Style TargetType="{x:Type TextBlock}">
	<Setter Property="FontFamily" Value=FontFamily="{DynamicResource MyWindowFontFamily}"/>
</Style>
```

で、プログラム起動時にWindowのコンストラクタとかApplicationのStartupとかで以下の様にすればいい。

```cs:外字の読み込み
〜〜〜

using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"EUDC\932"))
{
	var fontList = new List<string>() {"メイリオ"};

	var regKeyValue = (string)key.GetValue("SystemDefaultEUDCFont");

	if (string.Compare(regKeyValue, "EUDC.TTE", true) == 0)
	{
		fontList.Add(@"file:///C:/Windows/Fonts/#EUDC");
    }
    else
    {
		var EUDCDir = Path.GetDirectoryName(regKeyValue);
		fontList.Add(string.Format(@"file:///{0}", Path.Combine(EUDCDirPath, "#EUDC").Replace('¥¥', '/')));
	}

	this.Resources["MyWindowFontFamily"] = new FontFamily(string.Join(", ", fontList.ToArray()));
}

〜〜〜
```

これで外字の表示がファイル名やメモ帳と同じ表示になります。

これ、複数のプロジェクトやウィンドウで同じSytle適用するのを想定した作りです。
他にもFontFamily作成部分だけModelとして切り出して、ViewModelでFontFamilyのプロパティ公開したりして、各FontFamilyにBindingするって手もあるかと思います。

##というわけで

これでまぁほぼ問題無いでしょう。

外字フォントの指定先を弄る事で応用が効くのも想像つくのではないでしょうか。

他に、Windowsでは特定のフォントに紐付いた外字フォントの登録とかも可能になっています。
それによって設定されるレジストリの値も似た所にあるので、外字の読み込みプログラムを変更することで同様に対応出来るのではないかと。

この問題の原因に行き着くの結構大変でした。外字に悩まされる人が減ればいいな。