#title(Visual Studio International Pack: Japanese Text Alignment Libraryを使う) #navi(.NETプログラミング研究) #contents *Visual Studio International Pack: Japanese Text Alignment Libraryを使う [#s62cf216] Japanese Text Alignment Library(日本語テキスト整列ライブラリ)は、文字列を均等割付で描画するためのライブラリです("Japanese"となっていますが、それ以外の言語でも使用できそうです)。均等割付で文字列を描画すると、文字列の幅が描画領域の幅より短いとき、文字列内の適当な箇所に隙間を入れることによって、描画領域の幅いっぱいに文字列が描画されます。 文字列を描画するメソッドと言えばGraphics.DrawStringメソッドですが、Graphics.DrawStringメソッドの代わりに利用できるものと考えてよいでしょう。 **ダウンロードとインストール [#wd828c82] Japanese Text Alignment Libraryをインストールする手順は、[[第85号>../85]]で紹介した方法とほぼ同じです。インストールするMSIファイルは、"JPNTextAlign.msi "です。 また、プロジェクトの参照に「JapaneseTextAlignment.dll」を追加する必要があることも前号と同じです。具体的な方法は、「[[「○○○.dllを参照に追加します」の意味は?>http://dobon.net/vb/dotnet/help/addreference.html]]」をご覧ください。なおこのDLLがある場所は、デフォルトでは、「C:\Program Files\Microsoft Visual Studio International Pack\Japanese Text Alignment Library\Library」というフォルダです。 **Utility.DrawJapaneseStringメソッド [#w77799f2] はじめに断らせていただきますが、今号の内容も前号と同様に私独自の試行に基づく想像によって書かせていただいている部分が多々あります。よって、ここでの説明が正しいという保障はないということをご了承ください。 Japanese Text Alignment Libraryで文字列を描画するには、Utility.DrawJapaneseStringメソッドを使います。このメソッドはGraphics.DrawString(String, Font, Brush, RectangleF, StringFormat)メソッドに似ていますが、DrawJapaneseStringメソッドでは垂直方向の配置方法を指定できなかったり、右端で文字列を折り返して描画することができないなど違う点も多々あります。((これらの点では、Graphics.DrawString(String, Font, Brush, PointF)メソッドの方に近いかもしれません。)) 文字列を均等割付で描画する時、文字列内のどこに隙間を入れてどこに入れないかという規則をIAlignmentUnitInfoProviderインターフェイスを実装したクラスを使って変更することも出来ます。これを使えば、例えば、日本語の文字の間には均等な隙間を入れるが、英数字の間には隙間を入れないということもできます。 Japanese Text Alignment LibraryにはIAlignmentUnitInfoProviderを実装したクラスとして、SimpleAlignmentUnitInfoProviderとSmartAlignmentUnitInfoProviderの2つのクラスが用意されています。Utility.DrawJapaneseStringメソッドをIAlignmentUnitInfoProviderを指定しないで呼び出したときは、SimpleAlignmentUnitInfoProviderが使われます。この2つのクラスの違いについては、後述します。 **整列スタイルの違い [#ie424e36] Utility.DrawJapaneseStringメソッドのパラメータであるTextAlignmentStyle構造体のStyleプロパティにより、整列スタイルを指定します。指定できる整列スタイルは以下の通りです。この中でJustifyとFullJustify以外はGraphics.DrawStringメソッドでも可能ですので、あまり使わないでしょう。 |TextAlignmentStyle列挙体のメンバー|説明|h |Left|文字列が左に配置されるように指定する。| |Center|文字列が中央に配置されるように指定する。| |Right|文字列が右に配置されるように指定する。| |Justify|両端に間を空けないで文字列が均等割付で配置されるように指定する。| |FullJustify|両端に間を空けて文字列が均等割付で配置されるように指定する。| このような違い以外に、以下のような違いが確認できました。 -JustifyとFullJustifyでは改行文字で改行されませんが(スペース文字と同様に隙間は入る)、それ以外では改行されます。 具体的にこれらにどのような違いがあるかを調べる例を以下に示します。ここではフォームにピクチャーボックス"PictureBox1"が配置されており、このPaintイベントハンドラとして"PictureBox1_Paint"が追加されているものとします。 #code(vbnet){{ 'PictureBox1のPaintイベントハンドラ Private Sub PictureBox1_Paint(ByVal sender As System.Object, _ ByVal e As System.Windows.Forms.PaintEventArgs) _ Handles PictureBox1.Paint '表示する文字列 Dim str As String = ".NET Framework 3.5(Microsoft)で実行中" 'フォントと色 Dim f As New Font("MS UI Gothic", 12) Dim c As Color = Color.Black '描画する範囲を指定 Dim rect As New Rectangle(5, 5, 500, 30) 'TextAlignmentStyleInfoの作成 Dim align As New Microsoft.International.JapaneseTextAlignment.TextAlignmentStyleInfo() '範囲を表す四角を描画 e.Graphics.DrawRectangle(Pens.Red, rect) '整列スタイルをJustifyに align.Style = Microsoft.International.JapaneseTextAlignment.TextAlignmentStyle.Justify '文字列を描画する Microsoft.International.JapaneseTextAlignment.Utility.DrawJapaneseString( _ e.Graphics, str, f, c, rect, align) '範囲を表す四角を描画 rect.Y += rect.Height + 5 e.Graphics.DrawRectangle(Pens.Red, rect) '整列スタイルをFullJustifyに align.Style = Microsoft.International.JapaneseTextAlignment.TextAlignmentStyle.FullJustify '文字列を描画する Microsoft.International.JapaneseTextAlignment.Utility.DrawJapaneseString( _ e.Graphics, str, f, c, rect, align) '範囲を表す四角を描画 rect.Y += rect.Height + 5 e.Graphics.DrawRectangle(Pens.Red, rect) '整列スタイルをLeftに align.Style = Microsoft.International.JapaneseTextAlignment.TextAlignmentStyle.Left '文字列を描画する Microsoft.International.JapaneseTextAlignment.Utility.DrawJapaneseString( _ e.Graphics, str, f, c, rect, align) '範囲を表す四角を描画 rect.Y += rect.Height + 5 e.Graphics.DrawRectangle(Pens.Red, rect) '整列スタイルをRightに align.Style = Microsoft.International.JapaneseTextAlignment.TextAlignmentStyle.Right '文字列を描画する Microsoft.International.JapaneseTextAlignment.Utility.DrawJapaneseString( _ e.Graphics, str, f, c, rect, align) '範囲を表す四角を描画 rect.Y += rect.Height + 5 e.Graphics.DrawRectangle(Pens.Red, rect) '整列スタイルをCenterに align.Style = Microsoft.International.JapaneseTextAlignment.TextAlignmentStyle.Center '文字列を描画する Microsoft.International.JapaneseTextAlignment.Utility.DrawJapaneseString( _ e.Graphics, str, f, c, rect, align) '後始末 f.Dispose() End Sub }} #code(csharp){{ //PictureBox1のPaintイベントハンドラ private void PictureBox1_Paint(object sender, PaintEventArgs e) { //表示する文字列 string str = ".NET Framework 3.5(Microsoft)で実行中"; //フォントと色 Font f = new Font("MS UI Gothic", 12); Color c = Color.Black; //描画する範囲を指定 Rectangle rect = new Rectangle(5, 5, 500, 30); //TextAlignmentStyleInfoの作成 Microsoft.International.JapaneseTextAlignment.TextAlignmentStyleInfo align = new Microsoft.International.JapaneseTextAlignment.TextAlignmentStyleInfo(); //範囲を表す四角を描画 e.Graphics.DrawRectangle(Pens.Red, rect); //整列スタイルをJustifyに align.Style = Microsoft.International.JapaneseTextAlignment.TextAlignmentStyle.Justify; //文字列を描画する Microsoft.International.JapaneseTextAlignment.Utility.DrawJapaneseString( e.Graphics, str, f, c, rect, align); //範囲を表す四角を描画 rect.Y += rect.Height + 5; e.Graphics.DrawRectangle(Pens.Red, rect); //整列スタイルをFullJustifyに align.Style = Microsoft.International.JapaneseTextAlignment.TextAlignmentStyle.FullJustify; //文字列を描画する Microsoft.International.JapaneseTextAlignment.Utility.DrawJapaneseString( e.Graphics, str, f, c, rect, align); //範囲を表す四角を描画 rect.Y += rect.Height + 5; e.Graphics.DrawRectangle(Pens.Red, rect); //整列スタイルをLeftに align.Style = Microsoft.International.JapaneseTextAlignment.TextAlignmentStyle.Left; //文字列を描画する Microsoft.International.JapaneseTextAlignment.Utility.DrawJapaneseString( e.Graphics, str, f, c, rect, align); //範囲を表す四角を描画 rect.Y += rect.Height + 5; e.Graphics.DrawRectangle(Pens.Red, rect); //整列スタイルをRightに align.Style = Microsoft.International.JapaneseTextAlignment.TextAlignmentStyle.Right; //文字列を描画する Microsoft.International.JapaneseTextAlignment.Utility.DrawJapaneseString( e.Graphics, str, f, c, rect, align); //範囲を表す四角を描画 rect.Y += rect.Height + 5; e.Graphics.DrawRectangle(Pens.Red, rect); //整列スタイルをCenterに align.Style = Microsoft.International.JapaneseTextAlignment.TextAlignmentStyle.Center; //文字列を描画する Microsoft.International.JapaneseTextAlignment.Utility.DrawJapaneseString( e.Graphics, str, f, c, rect, align); //後始末 f.Dispose(); } }} このコードを実行した結果、以下のように表示されます。 &ref(JapaneseTextAlignment1.png); 上記の例では使いませんでしたが、TextAlignmentStyleInfo構造体のLeftMarginとRightMarginプロパティにより、左と右の余白を指定することも出来ます。 **SimpleAlignmentUnitInfoProviderとSmartAlignmentUnitInfoProviderの違い [#j3c2ace6] SimpleAlignmentUnitInfoProviderは最も単純なIAlignmentUnitInfoProviderで、全ての文字の間隔が同じになるように均等割付します。 SmartAlignmentUnitInfoProviderでは、日本語の文字の間には間隔が空きますが、英数字の間には間隔が空かないように均等割付します。具体的には、以下のような箇所に間隔が空きます(ヘルプからの抜粋)。 -前の文字がCJK統合漢字。 -前の文字が '(' (U+0028)でなく、現在の文字が '('。 -前の文字が ')' (U+0029)で、現在のキャラクタが ')'ではない。 -前の文字がアルファベット (a-z and A-Z), 数字 (0-9) および '('で、現在の文字が アルファベット, 数字および ')'ではない。 上記の例では、SimpleAlignmentUnitInfoProviderを使用していました。以下に今まで通りにSimpleAlignmentUnitInfoProviderで描画する例と、SmartAlignmentUnitInfoProviderを使って描画する例を示します。 #code(vbnet){{ 'PictureBox1のPaintイベントハンドラ Private Sub PictureBox1_Paint(ByVal sender As System.Object, _ ByVal e As System.Windows.Forms.PaintEventArgs) _ Handles PictureBox1.Paint '表示する文字列 Dim str As String = ".NET Framework 3.5(Microsoft)で実行中" 'フォントと色 Dim f As New Font("MS UI Gothic", 12) Dim c As Color = Color.Black '描画する範囲を指定 Dim rect As New Rectangle(5, 5, 500, 30) 'TextAlignmentStyleInfoの作成 Dim align As New Microsoft.International.JapaneseTextAlignment.TextAlignmentStyleInfo() '範囲を表す四角を描画 e.Graphics.DrawRectangle(Pens.Red, rect) '整列スタイルをJustifyに align.Style = Microsoft.International.JapaneseTextAlignment.TextAlignmentStyle.Justify 'SimpleAlignmentUnitInfoProviderで文字列を描画する Microsoft.International.JapaneseTextAlignment.Utility.DrawJapaneseString( _ e.Graphics, str, f, c, rect, align) '範囲を表す四角を描画 rect.Y += rect.Height + 5 e.Graphics.DrawRectangle(Pens.Red, rect) 'SmartAlignmentUnitInfoProviderの作成 Dim provider As Microsoft.International.JapaneseTextAlignment.IAlignmentUnitInfoProvider = _ New Microsoft.International.JapaneseTextAlignment.SmartAlignmentUnitInfoProvider() 'SmartAlignmentUnitInfoProviderで文字列を描画する Microsoft.International.JapaneseTextAlignment.Utility.DrawJapaneseString( _ e.Graphics, str, f, c, rect, align, _ provider) '後始末 f.Dispose() End Sub }} #code(csharp){{ //PictureBox1のPaintイベントハンドラ private void PictureBox1_Paint(object sender, PaintEventArgs e) { //表示する文字列 string str = ".NET Framework 3.5(Microsoft)で実行中"; //フォントと色 Font f = new Font("MS UI Gothic", 12); Color c = Color.Black; //描画する範囲を指定 Rectangle rect = new Rectangle(5, 5, 500, 30); //TextAlignmentStyleInfoの作成 Microsoft.International.JapaneseTextAlignment.TextAlignmentStyleInfo align = new Microsoft.International.JapaneseTextAlignment.TextAlignmentStyleInfo(); //範囲を表す四角を描画 e.Graphics.DrawRectangle(Pens.Red, rect); //整列スタイルをJustifyに align.Style = Microsoft.International.JapaneseTextAlignment.TextAlignmentStyle.Justify; //SimpleAlignmentUnitInfoProviderで文字列を描画する Microsoft.International.JapaneseTextAlignment.Utility.DrawJapaneseString( e.Graphics, str, f, c, rect, align); //範囲を表す四角を描画 rect.Y += rect.Height + 5; e.Graphics.DrawRectangle(Pens.Red, rect); //SmartAlignmentUnitInfoProviderの作成 Microsoft.International.JapaneseTextAlignment.IAlignmentUnitInfoProvider provider = new Microsoft.International.JapaneseTextAlignment.SmartAlignmentUnitInfoProvider(); //SmartAlignmentUnitInfoProviderで文字列を描画する Microsoft.International.JapaneseTextAlignment.Utility.DrawJapaneseString( e.Graphics, str, f, c, rect, align, provider); //後始末 f.Dispose(); } }} このコードを実行した結果、以下のように表示されます。 &ref(JapaneseTextAlignment2.png); このように、SimpleAlignmentUnitInfoProviderで描画したときは全ての文字の間に同じ幅の間隔が入りますが、SmartAlignmentUnitInfoProviderで描画したときは連続した英数字の間には間隔が入りません。 **IAlignmentUnitInfoProviderを実装して割り付け方を変更する [#q3643937] IAlignmentUnitInfoProviderを実装したクラスを作成して、どこに隙間を入れるかを変更する方法を紹介します。 Utility.DrawJapaneseStringメソッドで文字列を均等割付で描画するとき、文字列を「割付け単位」に分割します。同じ割付け単位にある文字列はくっついて表示されますが、割付け単位と割付け単位の間には隙間が入ります。この「割付け単位」を決定する方法を定義するのが、IAlignmentUnitInfoProviderです。 はじめの割付け単位の分割は自動的に行われ、スペース文字(改行文字も)で分割されます(スペース文字は削除され、描画されません)。例えば"This is a pen."という文字列であれば、"This"と"is"と"a"と"pen."という4つの割付け単位に分割されます。 ここからがIAlignmentUnitInfoProviderの仕事です。まずInitializeメソッドが呼び出され、はじめの割付け単位の先頭の文字がsパラメータに渡されます。もしその割付け単位に次の文字があれば(つまり2文字以上ならば)、次にIsNewAlignmentUnitメソッドが呼び出され、次の文字がsパラメータ渡されます。もしこの文字から新しい割付け単位にしたい(この文字の前で間隔をあけたい)と思うなら、IsNewAlignmentUnitメソッドでTrueを返します。そうでなければ、Falseを返します。 このように割付け単位の最後の文字まで1文字ずつIsNewAlignmentUnitメソッドが呼び出されていきます。最後の文字まで行ったら、次の割付け単位について、InitializeメソッドとIsNewAlignmentUnitメソッドの呼び出しが同じように行われます。 "This is a pen."の例で言えば、次のようになります。 +Initializeが呼び出され、"T"がパラメータとして渡されます。 +IsNewAlignmentUnitが呼び出され、"h"が渡されます。 +IsNewAlignmentUnitが呼び出され、"i"が渡されます。 +IsNewAlignmentUnitが呼び出され、"s"が渡されます。 +ここで新しい割付け単位になるため、Initializeが呼び出され、"i"が渡されます。 +IsNewAlignmentUnitが呼び出され、"s"が渡されます。 +次の割付け単位は"a"一文字なので、Initializeが呼び出され、"a"が渡されますが、Initializeは呼び出されません。 +Initializeが呼び出され、"p"が渡されます。 +IsNewAlignmentUnitが呼び出され、"e"が渡されます。 +IsNewAlignmentUnitが呼び出され、"n"が渡されます。 +IsNewAlignmentUnitが呼び出され、"."が渡されます。 少し分かりにくかったかもしれませんが、簡単に言えば、均等割付のときに隙間を入れる場所ではIsNewAlignmentUnitメソッドでTrueを返し、入れない場所ではFalseを返せばよいのです。 具体例を示します。以下に示すIAlignmentUnitInfoProviderを実装したクラスでは、英数字が連続している箇所では間隔を空けず、それ以外の場所では間隔を空けるようにしています。 #code(vbnet){{ 'IAlignmentUnitInfoProviderを実装したクラス Public Class TestAlignmentUnitInfoProvider Implements Microsoft.International.JapaneseTextAlignment.IAlignmentUnitInfoProvider '前の文字 Private previousString As String Public Sub Initialize(ByVal s As String) _ Implements Microsoft.International.JapaneseTextAlignment. _ IAlignmentUnitInfoProvider.Initialize If String.IsNullOrEmpty(s) Then Throw New ArgumentException("sがnullか空です。") End If '文字を覚えておく Me.previousString = s End Sub Public Function IsNewAlignmentUnit(ByVal s As String) As Boolean _ Implements Microsoft.International.JapaneseTextAlignment. _ IAlignmentUnitInfoProvider.IsNewAlignmentUnit If String.IsNullOrEmpty(s) Then Throw New ArgumentException("sがnullか空です。") End If '英数字以外かを判断する Dim reg As New System.Text.RegularExpressions.Regex( _ "^[^a-zA-Z0-9]", _ System.Text.RegularExpressions.RegexOptions.Compiled) '前の文字と今の文字が英数字のときだけ間隔をあけないようにする Dim ret As Boolean = reg.IsMatch(Me.previousString) OrElse reg.IsMatch(s) '前の文字を更新 Me.previousString = s '結果を返す Return ret End Function End Class }} #code(csharp){{ //IAlignmentUnitInfoProviderを実装したクラス public class TestAlignmentUnitInfoProvider : Microsoft.International.JapaneseTextAlignment.IAlignmentUnitInfoProvider { //前の文字 private string previousString; public void Initialize(string s) { if (string.IsNullOrEmpty(s)) throw new ArgumentException("sがnullか空です。"); //文字を覚えておく this.previousString = s; } public bool IsNewAlignmentUnit(string s) { if (string.IsNullOrEmpty(s)) throw new ArgumentException("sがnullか空です。"); //英数字以外かを判断する System.Text.RegularExpressions.Regex reg = new System.Text.RegularExpressions.Regex( "^[^a-zA-Z0-9]", System.Text.RegularExpressions.RegexOptions.Compiled); //前の文字と今の文字が英数字のときだけ間隔をあけないようにする bool ret = reg.IsMatch(this.previousString) || reg.IsMatch(s); //前の文字を更新 this.previousString = s; //結果を返す return ret; } } }} このクラスを使って文字列を均等割付で描画する例を示します。 #code(vbnet){{ 'PictureBox1のPaintイベントハンドラ Private Sub PictureBox1_Paint(ByVal sender As System.Object, _ ByVal e As System.Windows.Forms.PaintEventArgs) _ Handles PictureBox1.Paint '表示する文字列 Dim str As String = ".NET Framework 3.5(Microsoft)で実行中" 'フォントと色 Dim f As New Font("MS UI Gothic", 12) Dim c As Color = Color.Black '描画する範囲を指定 Dim rect As New Rectangle(5, 5, 500, 30) 'TextAlignmentStyleInfoの作成 Dim align As New Microsoft.International.JapaneseTextAlignment.TextAlignmentStyleInfo() '範囲を表す四角を描画 e.Graphics.DrawRectangle(Pens.Red, rect) '整列スタイルをJustifyに align.Style = Microsoft.International.JapaneseTextAlignment.TextAlignmentStyle.Justify 'TestAlignmentUnitInfoProviderの作成 Dim provider As New TestAlignmentUnitInfoProvider() 'TestAlignmentUnitInfoProviderで文字列を描画する Microsoft.International.JapaneseTextAlignment.Utility.DrawJapaneseString( _ e.Graphics, str, f, c, rect, align, provider) '後始末 f.Dispose() End Sub }} #code(csharp){{ //PictureBox1のPaintイベントハンドラ private void PictureBox1_Paint(object sender, PaintEventArgs e) { //表示する文字列 string str = ".NET Framework 3.5(Microsoft)で実行中"; //フォントと色 Font f = new Font("MS UI Gothic", 12); Color c = Color.Black; //描画する範囲を指定 Rectangle rect = new Rectangle(5, 5, 500, 30); //TextAlignmentStyleInfoの作成 Microsoft.International.JapaneseTextAlignment.TextAlignmentStyleInfo align = new Microsoft.International.JapaneseTextAlignment.TextAlignmentStyleInfo(); //範囲を表す四角を描画 e.Graphics.DrawRectangle(Pens.Red, rect); //整列スタイルをJustifyに align.Style = Microsoft.International.JapaneseTextAlignment.TextAlignmentStyle.Justify; //TestAlignmentUnitInfoProviderの作成 TestAlignmentUnitInfoProvider provider = new TestAlignmentUnitInfoProvider(); //TestAlignmentUnitInfoProviderで文字列を描画する Microsoft.International.JapaneseTextAlignment.Utility.DrawJapaneseString( e.Graphics, str, f, c, rect, align, provider); //後始末 f.Dispose(); } }} 結果は、以下の図のようになります。 &ref(JapaneseTextAlignment3.png); **スペース文字の部分だけに隙間を入れる [#af06ea47] スペース文字の部分だけに隙間が入るようにして均等割付で描画するには、IAlignmentUnitInfoProviderのIsNewAlignmentUnitメソッドで常にFalseを返すようにすれば良いということは今までの説明からお分かりいただけるでしょう。 これとは逆にIsNewAlignmentUnitメソッドで常にTrueを返すのが、SimpleAlignmentUnitInfoProviderクラスです。よってSimpleAlignmentUnitInfoProviderクラスを使うと、全ての文字の間に隙間が入ります。 **コメント [#y3b5a8d6] #comment //これより下は編集しないでください #pageinfo([[:Category/.NET]] [[:Category/ASP.NET]],2008-12-08 (月) 03:30:30,DOBON!,2008-12-08 (月) 03:32:41,DOBON!) |