Visual Studio International Pack: Japanese Text Alignment Libraryを使う †
Japanese Text Alignment Library(日本語テキスト整列ライブラリ)は、文字列を均等割付で描画するためのライブラリです("Japanese"となっていますが、それ以外の言語でも使用できそうです)。均等割付で文字列を描画すると、文字列の幅が描画領域の幅より短いとき、文字列内の適当な箇所に隙間を入れることによって、描画領域の幅いっぱいに文字列が描画されます。
文字列を描画するメソッドと言えばGraphics.DrawStringメソッドですが、Graphics.DrawStringメソッドの代わりに利用できるものと考えてよいでしょう。
ダウンロードとインストール †
Japanese Text Alignment Libraryをインストールする手順は、第85号で紹介した方法とほぼ同じです。インストールするMSIファイルは、"JPNTextAlign.msi "です。
また、プロジェクトの参照に「JapaneseTextAlignment.dll」を追加する必要があることも前号と同じです。具体的な方法は、「「○○○.dllを参照に追加します」の意味は?」をご覧ください。なおこのDLLがある場所は、デフォルトでは、「C:\Program Files\Microsoft Visual Studio International Pack\Japanese Text Alignment Library\Library」というフォルダです。
Utility.DrawJapaneseStringメソッド †
はじめに断らせていただきますが、今号の内容も前号と同様に私独自の試行に基づく想像によって書かせていただいている部分が多々あります。よって、ここでの説明が正しいという保障はないということをご了承ください。
Japanese Text Alignment Libraryで文字列を描画するには、Utility.DrawJapaneseStringメソッドを使います。このメソッドはGraphics.DrawString(String, Font, Brush, RectangleF, StringFormat)メソッドに似ていますが、DrawJapaneseStringメソッドでは垂直方向の配置方法を指定できなかったり、右端で文字列を折り返して描画することができないなど違う点も多々あります。*1
文字列を均等割付で描画する時、文字列内のどこに隙間を入れてどこに入れないかという規則をIAlignmentUnitInfoProviderインターフェイスを実装したクラスを使って変更することも出来ます。これを使えば、例えば、日本語の文字の間には均等な隙間を入れるが、英数字の間には隙間を入れないということもできます。
Japanese Text Alignment LibraryにはIAlignmentUnitInfoProviderを実装したクラスとして、SimpleAlignmentUnitInfoProviderとSmartAlignmentUnitInfoProviderの2つのクラスが用意されています。Utility.DrawJapaneseStringメソッドをIAlignmentUnitInfoProviderを指定しないで呼び出したときは、SimpleAlignmentUnitInfoProviderが使われます。この2つのクラスの違いについては、後述します。
整列スタイルの違い †
Utility.DrawJapaneseStringメソッドのパラメータであるTextAlignmentStyle構造体のStyleプロパティにより、整列スタイルを指定します。指定できる整列スタイルは以下の通りです。この中でJustifyとFullJustify以外はGraphics.DrawStringメソッドでも可能ですので、あまり使わないでしょう。
TextAlignmentStyle列挙体のメンバー | 説明 | Left | 文字列が左に配置されるように指定する。 | Center | 文字列が中央に配置されるように指定する。 | Right | 文字列が右に配置されるように指定する。 | Justify | 両端に間を空けないで文字列が均等割付で配置されるように指定する。 | FullJustify | 両端に間を空けて文字列が均等割付で配置されるように指定する。 |
このような違い以外に、以下のような違いが確認できました。
- JustifyとFullJustifyでは改行文字で改行されませんが(スペース文字と同様に隙間は入る)、それ以外では改行されます。
具体的にこれらにどのような違いがあるかを調べる例を以下に示します。ここではフォームにピクチャーボックス"PictureBox1"が配置されており、このPaintイベントハンドラとして"PictureBox1_Paint"が追加されているものとします。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
| | 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)
Dim align As New Microsoft.International.JapaneseTextAlignment.TextAlignmentStyleInfo()
e.Graphics.DrawRectangle(Pens.Red, rect)
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)
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)
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)
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)
align.Style = Microsoft.International.JapaneseTextAlignment.TextAlignmentStyle.Center
Microsoft.International.JapaneseTextAlignment.Utility.DrawJapaneseString( _
e.Graphics, str, f, c, rect, align)
f.Dispose()
End Sub
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
| | 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);
Microsoft.International.JapaneseTextAlignment.TextAlignmentStyleInfo align =
new Microsoft.International.JapaneseTextAlignment.TextAlignmentStyleInfo();
e.Graphics.DrawRectangle(Pens.Red, rect);
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);
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);
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);
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);
align.Style = Microsoft.International.JapaneseTextAlignment.TextAlignmentStyle.Center;
Microsoft.International.JapaneseTextAlignment.Utility.DrawJapaneseString(
e.Graphics, str, f, c, rect, align);
f.Dispose();
}
|
このコードを実行した結果、以下のように表示されます。
上記の例では使いませんでしたが、TextAlignmentStyleInfo構造体のLeftMarginとRightMarginプロパティにより、左と右の余白を指定することも出来ます。
SimpleAlignmentUnitInfoProviderとSmartAlignmentUnitInfoProviderの違い †
SimpleAlignmentUnitInfoProviderは最も単純なIAlignmentUnitInfoProviderで、全ての文字の間隔が同じになるように均等割付します。
SmartAlignmentUnitInfoProviderでは、日本語の文字の間には間隔が空きますが、英数字の間には間隔が空かないように均等割付します。具体的には、以下のような箇所に間隔が空きます(ヘルプからの抜粋)。
- 前の文字がCJK統合漢字。
- 前の文字が '(' (U+0028)でなく、現在の文字が '('。
- 前の文字が ')' (U+0029)で、現在のキャラクタが ')'ではない。
- 前の文字がアルファベット (a-z and A-Z), 数字 (0-9) および '('で、現在の文字が アルファベット, 数字および ')'ではない。
上記の例では、SimpleAlignmentUnitInfoProviderを使用していました。以下に今まで通りにSimpleAlignmentUnitInfoProviderで描画する例と、SmartAlignmentUnitInfoProviderを使って描画する例を示します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
| | 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)
Dim align As New Microsoft.International.JapaneseTextAlignment.TextAlignmentStyleInfo()
e.Graphics.DrawRectangle(Pens.Red, rect)
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)
Dim provider As Microsoft.International.JapaneseTextAlignment.IAlignmentUnitInfoProvider = _
New Microsoft.International.JapaneseTextAlignment.SmartAlignmentUnitInfoProvider()
Microsoft.International.JapaneseTextAlignment.Utility.DrawJapaneseString( _
e.Graphics, str, f, c, rect, align, _
provider)
f.Dispose()
End Sub
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| | 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);
Microsoft.International.JapaneseTextAlignment.TextAlignmentStyleInfo align =
new Microsoft.International.JapaneseTextAlignment.TextAlignmentStyleInfo();
e.Graphics.DrawRectangle(Pens.Red, rect);
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);
Microsoft.International.JapaneseTextAlignment.IAlignmentUnitInfoProvider provider =
new Microsoft.International.JapaneseTextAlignment.SmartAlignmentUnitInfoProvider();
Microsoft.International.JapaneseTextAlignment.Utility.DrawJapaneseString(
e.Graphics, str, f, c, rect, align, provider);
f.Dispose();
}
|
このコードを実行した結果、以下のように表示されます。
このように、SimpleAlignmentUnitInfoProviderで描画したときは全ての文字の間に同じ幅の間隔が入りますが、SmartAlignmentUnitInfoProviderで描画したときは連続した英数字の間には間隔が入りません。
IAlignmentUnitInfoProviderを実装して割り付け方を変更する †
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を実装したクラスでは、英数字が連続している箇所では間隔を空けず、それ以外の場所では間隔を空けるようにしています。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
| | 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
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| | 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;
}
}
|
このクラスを使って文字列を均等割付で描画する例を示します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| | 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)
Dim align As New Microsoft.International.JapaneseTextAlignment.TextAlignmentStyleInfo()
e.Graphics.DrawRectangle(Pens.Red, rect)
align.Style = Microsoft.International.JapaneseTextAlignment.TextAlignmentStyle.Justify
Dim provider As New TestAlignmentUnitInfoProvider()
Microsoft.International.JapaneseTextAlignment.Utility.DrawJapaneseString( _
e.Graphics, str, f, c, rect, align, provider)
f.Dispose()
End Sub
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| | 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);
Microsoft.International.JapaneseTextAlignment.TextAlignmentStyleInfo align =
new Microsoft.International.JapaneseTextAlignment.TextAlignmentStyleInfo();
e.Graphics.DrawRectangle(Pens.Red, rect);
align.Style = Microsoft.International.JapaneseTextAlignment.TextAlignmentStyle.Justify;
TestAlignmentUnitInfoProvider provider = new TestAlignmentUnitInfoProvider();
Microsoft.International.JapaneseTextAlignment.Utility.DrawJapaneseString(
e.Graphics, str, f, c, rect, align, provider);
f.Dispose();
}
|
結果は、以下の図のようになります。
スペース文字の部分だけに隙間を入れる †
スペース文字の部分だけに隙間が入るようにして均等割付で描画するには、IAlignmentUnitInfoProviderのIsNewAlignmentUnitメソッドで常にFalseを返すようにすれば良いということは今までの説明からお分かりいただけるでしょう。
これとは逆にIsNewAlignmentUnitメソッドで常にTrueを返すのが、SimpleAlignmentUnitInfoProviderクラスです。よってSimpleAlignmentUnitInfoProviderクラスを使うと、全ての文字の間に隙間が入ります。
コメント †
これらの点では、Graphics.DrawString(String, Font, Brush, PointF)メソッドの方に近いかもしれません。
|