游ゴシック/游明朝を正しく使うには?[Windows 8.1ストア・アプリ開発]:WinRT/Metro TIPS
Windows 8.1に搭載された新しいフォント「游ゴシック」と「游明朝」。しかしWindowsストア・アプリで使う場合には落とし穴がある。その問題点と回避方法を解説する。
powered by Insider.NET
Windows 8.1(以降、Win 8.1)には、「游(ゆう)ゴシック」と「游明朝」*1という新しいフォントが搭載された。それらのフォントをWindowsストア・アプリで使いたいと考える開発者も少なくないだろう。ところが、うかつに使うと落とし穴があるのだ。本稿では、その問題を解説し、回避する方法を説明する。
事前準備
Win 8.1アプリを開発するには、Win 8.1とVisual Studio 2013(以降、VS 2013)が必要である。本稿では、Oracle VM VirtualBox上で64bit版Windows 8.1 Pro(製品版)とVisual Studio Express 2013 for Windows*2を使用している。なお、回避する方法を確かめるだけならPreview版でも可能だ。Preview版を準備する方法や注意事項は、「WinRT/Metro TIPS:Win8用のソース・コードをWin8.1用に変換するには?[Windows 8.1ストア・アプリ開発]」の記事をご参照いただきたい。
*1 游明朝フォントはWin 8.1の製品版で搭載された。Preview版には含まれていない。そのため、本稿では製品版を使って説明する。Preview版で試す場合には、コード中の「Yu Mincho」(游明朝)を「Yu Gothic」(游ゴシック)に置き換えてもらいたい。游明朝と游ゴシックでは合字の実装内容に違いがあるものの、基本的には同じだ。
*2 マイクロソフト公式ダウンロード・センターの「Microsoft Visual Studio Express 2013 for Windows」から入手できる。
「游ゴシック」と「游明朝」
Win 8.1で新しく追加されたフォントに、「游ゴシック」と「游明朝」がある(次の画像)。
この新しいフォントはコンテンツの表示に最適化されており、標準メールやカレンダーといったアプリでも使われているという*3。ならば自分のアプリにも使いたいと考える開発者も少なくないだろう。しかし、コンテンツの表示に最適ということは、UI要素の表示には最適ではないということだ。TextBlockやRichTextBlockなどのUI要素に使うと、何か問題が起きるのだろうか?
*3 10月16日付け報道「発売直前。一般公開までに知っておきたいWindows 8.1」による。
「随意合字」問題
OpenTypeフォントには「合字」(ligature)という機能がある。これは、表示するときに特定の一連の文字を1つのグリフに置き換える機能のことだ。OpenTypeの合字には次の表の4種類がある。
合字名称(カッコ内は英語名、タグ) | 説明 | 既定値 |
---|---|---|
標準合字 (Standard ligatures、liga) |
読みやすさが向上するようにデザインされた合字。 「fi」「fl」「ff」「ffi」など |
True |
コンテキスト合字 (Contextual ligatures、clig) |
合字を構成する文字間の結合動作を改良して、読みやすさが向上するようにデザインされている | True |
随意合字 (Discretionary ligatures、dlig) |
装飾的なデザインであり、読みやすさを向上させるためにデザインされているわけではない | False |
歴史的合字 (Historical ligatures、hlig) |
歴史的なデザインであり、読みやすさを向上させるためにデザインされているわけではない | False |
MSDNの「OpenType フォントの機能〜合字」などを基に作成した。なお、「タグ」はフォント・ファイルに指定を埋め込むときに使うタグで、OpenTypeの専門的な文書ではよく使われている。タグの一覧は「OpenType Layout tag registry 〜 Features listed alphabetically」にある。また、「既定値」はTypographyクラス(Windows.UI.Xaml.Documents名前空間)のオブジェクトを生成したときの既定値(上記「OpenTypeフォントの機能」のページ末尾を参照*4)。
*4 「OpenTypeフォントの機能」のページで説明されているのは、System.Windows.Documents名前空間(.NET Framework)にあるTypographyクラスだ。Windows.UI.Xaml.Documents名前空間(Windows Runtime)に持ってくるときに既定値を変えている可能性がないとはいえないが、筆者がいくつか確認してみたところでは同じだった。
合字のことは初めて聞いたという読者も多いだろう。取りあえずは、上の表の最初の2つ(ligaとclig)は読みやすくなり、残りの2つ(dligとhlig)は読みにくくなるものだと理解しておいてほしい。それでは、合字によってどんなことが起きるのか、実際にコードを書いて説明していこう。
VS 2013で新しいプロジェクトを作り、[基本ページ]を追加する。その<Page.Resources>要素内の文字列を「Officeファミリー」に書き換えて、ページのタイトルを変更する(次の画像)。
タイトルを変更した(XAMLエディタ)
フォントは既定値のまま。すなわち、半角英字はSegou UIフォントで、かな漢字はMeiryo UIフォント。英字とカナで、線の太さがずいぶん違う。
なお、上のXAMLコードでは<Page>要素の開始タグに属性として「RequestedTheme="Light"」を追加している。
ページのタイトルを表示している部分のコードは次のようになっている。
<TextBlock x:Name="pageTitle" Text="{StaticResource AppName}" Style="{StaticResource HeaderTextBlockStyle}" Grid.Column="1"
IsHitTestVisible="false" TextWrapping="NoWrap" VerticalAlignment="Bottom" Margin="0,0,30,40"/>
フォントを指定していないので、既定値のフォント(半角英字にはSegoe UIフォント、かな漢字にはMeiryo UIフォント)が適用されている。では、このタイトルのタグ(<TextBlock>要素)をまねていくつか<TextBlock>要素を書き、そこに游明朝フォントなどを適用してみよう(次のコード)。タイトルのタグ(<TextBlock>要素)には、「Style="{StaticResource HeaderTextBlockStyle}"」という属性がある。また、[新しい項目の追加]ダイアログから追加できる[分割ページ]項目テンプレートや[グループ詳細ページ]項目テンプレートの見出しと本文には、「Style="{StaticResource CaptionTextBlockStyle}"」と「Style="{StaticResource BodyTextBlockStyle}"」という属性が付いている。これらのStyle属性を付けるのが標準的だと、マイクロソフトは考えているということだろう。それらの標準的なStyle属性を次のコードにも適用してある。
……省略……
<TextBlock x:Name="pageTitle" Text="{StaticResource AppName}" Style="{StaticResource HeaderTextBlockStyle}"
Grid.Column="1"
IsHitTestVisible="false" TextWrapping="NoWrap" VerticalAlignment="Bottom" Margin="0,0,30,40"/>
</Grid>
<StackPanel Grid.Row="1" Margin="120,0,0,0">
<TextBlock Style="{StaticResource CaptionTextBlockStyle}" Margin="0,-30,0,8" FontFamily="Yu Mincho">
游明朝
</TextBlock>
<TextBlock Text="{StaticResource AppName}" Style="{StaticResource HeaderTextBlockStyle}"
IsHitTestVisible="false" TextWrapping="NoWrap" VerticalAlignment="Bottom" Margin="0,0,30,40"
FontFamily="Yu Mincho"
/>
<TextBlock Style="{StaticResource BodyTextBlockStyle}" FontFamily="Yu Mincho">
デフォルトでは、Segoe UI(英数字)とMeiryo UI(かな/漢字)の組み合わせになっている。
……省略……
</TextBlock>
</StackPanel>
……省略……
[基本ページ]に、太字の部分を追加する。「FontFamily="Yu Mincho"」という属性指定が、游明朝フォントの指定だ。
実行してみると、次の画像のようになる。游明朝のほかに、Meiryo UI/メイリオ/游ゴシックも追加してある。
上のコードの実行結果
1行目の「Officeファミリー」は、自動生成されたままのタイトルのタグ(<TextBlock>要素)。
2行目には、自動生成されたタイトルのタグに游明朝フォントの指定を追加した(x:Name属性は削った)。この後に続く文章は、「Style="{StaticResource BodyTextBlockStyle}"」という属性と游明朝フォントの指定。いずれも「ミリ」が、いわゆる環境依存文字に置き換えられてしまっている。
後半の3行続く「Officeファミリー」は、上から順にMeiryo UIフォント、メイリオ・フォント、游ゴシック・フォントの指定をタイトルのタグに追加した。
ちなみに、2行目(游明朝)の「Office」では「fi」が結合している。さらによく見ると、「O」の後ろの「f」とその次(「ice」の前)の「f」は形が違う(上部の曲がり具合に注目)。この「fi」は標準合字である。前出の表に示したように標準合字を使う指定はデフォルトで有効になっており、游明朝には標準合字が実装されているのだ。
なんと! 游明朝/メイリオ/游ゴシックの各フォントを指定されたテキストが文字化けしている!? タイトルをコピー&ペーストしてフォント指定を追加しただけなのに。
実は、これは文字化けではなく、「ミリ」の2文字が随意合字として別のグリフに置き換えられているのだ。すなわち、どこかで随意合字が指定されているのである。そして、合字が生じる文字列を偶然に表示するまでは気付かない。これが、游ゴシック/游明朝という新しく追加されたフォント(とメイリオ)をUI要素の表示に使った場合の落とし穴だ。では、随意合字はどこで指定されているのだろう。
随意合字はどこで指定されているのか?
XAMLエディタで、上のコードの「HeaderTextBlockStyle」の中にカーソルを置いて[F12]キーを押す。すると、「HeaderTextBlockStyle」の定義箇所にジャンプする。それは「generic.xaml」というファイルの9196行目付近にあるスタイル定義だ。HeaderTextBlockStyleの定義は、その直上にある「BaseTextBlockStyle」という別のスタイルをベースにしている(BasedOn属性)。それらを次のコードに示す。
<Style x:Key="BaseTextBlockStyle" TargetType="TextBlock">
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}"/>
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}"/>
<Setter Property="SelectionHighlightColor" Value="{ThemeResource TextSelectionHighlightColorThemeBrush}" />
<Setter Property="TextTrimming" Value="CharacterEllipsis"/>
<Setter Property="TextWrapping" Value="Wrap"/>
<Setter Property="Typography.StylisticSet20" Value="True"/>
<Setter Property="Typography.DiscretionaryLigatures" Value="True"/>
<Setter Property="Typography.CaseSensitiveForms" Value="True"/>
<Setter Property="LineHeight" Value="20"/>
<Setter Property="LineStackingStrategy" Value="BlockLineHeight"/>
<Setter Property="TextLineBounds" Value="TrimToBaseline"/>
<Setter Property="OpticalMarginAlignment" Value="TrimSideBearings"/>
</Style>
<Style x:Key="HeaderTextBlockStyle" TargetType="TextBlock" BasedOn="{StaticResource BaseTextBlockStyle}">
<Setter Property="FontSize" Value="56"/>
<Setter Property="FontWeight" Value="Light"/>
<Setter Property="LineHeight" Value="40"/>
</Style>
「BaseTextBlockStyle」の中でTypography.DiscretionaryLigaturesプロパティがTrueに設定されている。
「BaseTextBlockStyle」の中で、「Typography.DiscretionaryLigatures」プロパティを「True」に設定している。これが、随意合字(Discretionary ligatures)の指定である。
ちなみに、「BaseTextBlockStyle」といういかにも標準的であるかのような名前のスタイル定義の中で、随意合字という特殊な合字がなぜ指定されているのかというと、Segoe UIフォントの歴史的な経緯によるものらしい。MSDNの「文字体裁のガイドライン」によれば、Segoe UIフォントを作るときに「標準合字をSegoe UIの随意の合字としてエンコード」したとのこと。つまり、読みやすさを向上させるための標準合字(前掲の表を参照)の機能を、装飾を目的としたはずの随意合字として実装してしまったのだ。そして、Segoe UIフォントをWindowsストア・アプリのUI要素の標準フォントとしたために、ベースとなるスタイル指定で随意合字を既定で有効にせざるを得なくなったのだろう。この結果、游明朝と游ゴシックでも随意合字が有効となり、別グリフへの置き換えが発生してしまったのだ。
随意合字を無効にするには?
コントロールのTypography.DiscretionaryLigaturesプロパティにfalseを指定すればよい。
上で、generic.xamlファイルに随意合字が指定されていることを明らかにした。しかし、generic.xamlファイルはプラットフォーム側のファイルであり、変更してはいけない(変更してしまうと、別のPCでビルドしたときに異なる結果になってしまう)。そこで、分かりやすく修正するには、次のコードのようにコントロールごとにTypography.DiscretionaryLigatures属性を付けてFalseを指定する。これでgeneric.xamlファイルでの指定を上書きできるのだ。
……省略……
<TextBlock x:Name="pageTitle" Text="{StaticResource AppName}" Style="{StaticResource HeaderTextBlockStyle}"
Grid.Column="1"
IsHitTestVisible="false" TextWrapping="NoWrap" VerticalAlignment="Bottom" Margin="0,0,30,40"
FontFamily="Yu Mincho" Typography.DiscretionaryLigatures="False"
/>
</Grid>
<StackPanel Grid.Row="1" Margin="120,0,0,0">
……省略……
<TextBlock Text="{StaticResource AppName}" Style="{StaticResource HeaderTextBlockStyle}"
IsHitTestVisible="false" TextWrapping="NoWrap" VerticalAlignment="Bottom" Margin="0,0,30,40"
FontFamily="Yu Mincho" Typography.DiscretionaryLigatures="False"
Typography.StandardLigatures="False"
/>
……省略……
</StackPanel>
1つ目の<TextBlock>要素はページのタイトル部分の指定だ。2つ目の<TextBlock>要素はその下に追加した「Officeファミリー」の指定だ。どちらでも随意合字を無効化している。また、2つ目の<TextBlock>要素では「Typography.StandardLigatures="False"」も指定した(後述)。
実行してみると、次の画像のように随意合字されずに正しく表示される(上記のコードのほかにも、さらに<TextBlock>要素を追加してある)。
上のコードの実行結果
随意合字の指定を取り消せば、フォントに游明朝を指定した場合でも正しく表示される。
さらに「Typography.StandardLigatures="False"」という属性を追加すれば、標準合字も無効になる(2段目の「fi」に注目)。
今回のTIPSは、基本的には以上だ。「Typography. DiscretionaryLigatures="False"」という属性を追加することさえ覚えておけば、游明朝や游ゴシックなどを正しく表示できる。
さて、以降は、「スタイル定義リソースの定義スコープ」を把握したうえで、効率よく属性を変更する方法について解説していく。上記の方法だけで十分だという方は、読み飛ばして「まとめ」に進んでもらって構わない。
generic.xamlの指定を上書きするには?
上のようにそれぞれのコントロールに属性を追加する方法は、面倒だしミスもしやすいだろう。generic.xamlファイルのスタイル定義を上書きできないだろうか?
ちょっと難しいが、やってみよう。それには、スタイル定義リソースの定義スコープの機能を利用する(次の図)。
スタイル定義リソースの定義スコープの概要図
定義スコープが異なれば、同じキー名を定義できる。例えば、プラットフォーム・リソースであるgeneric.xamlファイルとアプリ・リソースであるApp.xamlファイルには、同じ名前のキーが存在してよい(実行時には内側(=上の例ではApp.xamlファイル内)の定義が使用される)。
同じ定義スコープの中では、キー名は一意でなければならない。例えば、アプリ・リソースであるApp.xamlファイルとリソース・ディクショナリ・ファイルに同じ名前のキーは存在できない。
正確には、定義スコープが異なっていれば同じキー名があってもバッティングしないことを利用するのだ。generic.xamlの定義はプラットフォーム・リソースであり、スコープが違えば(=アプリ・リソースなどでは)同じキー名のリソースを定義できるのである。
例として、「HeaderTextBlockStyle」をアプリ・リソースにも定義してみよう。同じキー名のリソースが、プラットフォーム・リソースとアプリ・リソースの2箇所に存在することになる。ところで、実行時にリソースが検索される順序は、上の図の内側(=コントロール固有のリソース定義)から外側(=プラットフォーム・リソース)の順である。そこで、実行時には、アプリ・リソースに新しく定義した「HeaderTextBlockStyle」の方が使われることになる。
実際にやってみよう。アプリ・リソースはApp.xamlファイルに記述してもよいが、ほかのプロジェクトでも使い回しできるように、新しくクラス・ライブラリのプロジェクトを作り、その中にリソース・ディクショナリ・ファイルを置いて記述してみよう。
ソリューションに、新しい[クラス ライブラリ (Windowsストア・アプリ)]プロジェクトを追加し、「MyLibrary」と名前を付ける。MyLibraryプロジェクトに、新しい項目として[リソース ディクショナリ]を追加し、ファイル名を「MyStandardStyles01.xaml」とする。そうしたら、前述した手順で[F12]キーを使ってgeneric.xamlファイルの「HeaderTextBlockStyle」定義箇所を開き、その直上の「BaseTextBlockStyle」定義と合わせて、MyStandardStyles01.xamlファイルにコピーする。コピーした定義の中で、Typography.DiscretionaryLigaturesプロパティをFalseに変更する(次のコード)。
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyLibrary">
<!-- コピー範囲(ここから) -->
<!-- BaseTextBlockStyleを再定義 -->
<!-- 注意:さらに、このスタイルを継承しているスタイルを、個別に上書きしなければならない。 -->
<Style x:Key="BaseTextBlockStyle" TargetType="TextBlock">
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}"/>
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}"/>
……省略……
<!--<Setter Property="Typography.DiscretionaryLigatures" Value="True"/>-->
<!-- ↓随意合字をOFFにする -->
<Setter Property="Typography.DiscretionaryLigatures" Value="False"/>
……省略……
</Style>
<!-- ページ・タイトルで使われるスタイルを上書き -->
<!-- 注意:generic.xamlの定義と全く同じだが、上で再定義したBaseTextBlockStyleを使わせるために必要。 -->
<Style x:Key="HeaderTextBlockStyle" TargetType="TextBlock" BasedOn="{StaticResource BaseTextBlockStyle}">
<Setter Property="FontSize" Value="56"/>
<Setter Property="FontWeight" Value="Light"/>
<Setter Property="LineHeight" Value="40"/>
</Style>
<!-- コピー範囲(ここまで) -->
</ResourceDictionary>
ここで「ContentControlThemeFontFamily」も上書きしてフォントを全て置き換えてしまうことも可能だ。しかし、それはアプリごと、あるいはページごとに設定すべきことだろうから、ここでは行わない(後述のApp.xamlファイルを参照)。
次に、上で作成したリソース・ディクショナリをアプリに組み込む。そのためには、元のプロジェクトからMyLibraryプロジェクトに参照設定をし、App.xamlファイルを編集する。
まず、MyLibraryプロジェクトへの参照設定をする。ソリューション・エクスプローラでは、次の画像のようになる。
App.xamlファイルには、次のコードのように<Application.Resources>要素を追加する。
<Application
x:Class="MetroTips056_03_OverrideStyle.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MetroTips056_03_OverrideStyle">
<Application.Resources>
<ResourceDictionary >
<ResourceDictionary.MergedDictionaries>
<!-- generic.xamlを上書きするリソース・ディクショナリを読み込む -->
<ResourceDictionary Source="/MyLibrary/MyStandardStyles01.xaml" />
<ResourceDictionary>
<!-- さらに、標準で使われるフォントを上書き -->
<FontFamily x:Key="ContentControlThemeFontFamily">Yu Mincho</FontFamily>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
クラス・ライブラリ中のXAMLファイルを指定するには、このように「/{クラス名(=プロジェクト名)}/{ファイル名}」とする。また、generic.xamlファイルで定義されている「ContentControlThemeFontFamily」を、ここで上書きして游明朝に変えた。
以上だ。タイトルのタグ(<TextBlock>要素)から、FontFamily属性とTypography.DiscretionaryLigatures属性を削除してから、実行してみてほしい。タイトルのタグ(<TextBlock>要素)は自動生成されたままなのだが、表示されるフォントは游明朝に変わっているはずだ。
このようにして共通のプロジェクトにスタイル・リソースを定義しておけば、複数のプロジェクトから使える。ここで、共通の定義を作るとそのサイズが膨れ上がりがちで、起動時にリソース読み込みの時間が掛かるようになるのではと心配されるかもしれない。その点は、Win 8.1ではリソースが明確に要求された時点でリソースを作成するように改善されているので、心配無用だ*5。
ところで、上記のようにコピペする方法は、分かりやすいかもしれないが気に食わないという人もいるだろう。そこで、少々裏技っぽいのだが、BasedOnプロパティが参照するのは定義済みのスタイル(原文「defined style」)であることを利用した方法も紹介しておこう。これから生成しようとしているスタイル(=定義済みのスタイルではない)に同じ名前のキー名を付けても循環参照にはならないのだ*6。すると、前述のMyStandardStyles01.xamlファイルは、次のように書き直せる。
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyLibrary">
<!-- ページ・タイトルで使われるスタイルを上書き -->
<Style x:Key="HeaderTextBlockStyle" TargetType="TextBlock" BasedOn="{StaticResource HeaderTextBlockStyle}" >
<!-- 随意合字をOFFに -->
<Setter Property="Typography.DiscretionaryLigatures" Value="False"/>
</Style>
</ResourceDictionary>
BasedOn属性で参照しているHeaderTextBlockStyleは、すでに定義済みのスタイル。この場合は、プラットフォーム・リソースで定義されているHeaderTextBlockStyleになる。
x:Key属性で指定しているHeaderTextBlockStyleは、このスタイル定義自体に付ける名前。
なお、Preview版では、App.xamlファイルでのContentControlThemeFontFamilyの上書きが効かず、フォントが既定のままとなる。
*6 理屈のうえではそうなるはずだし、実際に実行できる。ただし、VS 2013が警告を出すことがあるので、「少々裏技っぽい」方法だとした。
まとめ
generic.xamlファイルに定義されているテキストブロックなどのスタイルには、随意合字の指定が含まれている。Meiryo UIフォント以外の日本語フォントには随意合字が実装されているものがあり(游明朝や游ゴシックなど)、そのままではまるで文字化けしたような表示になってしまう。
随意合字を解除するには、Typography.DiscretionaryLigatures属性に「False」を設定すればよい。コントロールごとに設定してもよいし、考え方は少々難しくなるがリソース・ディクショナリを作ってgeneric.xamlファイルでの定義を上書きしてもよい。
合字やスタイル定義リソースについては、次のドキュメントも参照してほしい。
Copyright© Digital Advantage Corp. All Rights Reserved.