デベロッパー
デベロッパー
.NETでのテキスト処理技法をマスターする
はじめに
Visual Basic 6でテキストを描画するのは、プロセスも結果も非常に単純でした。まずフォームまたはPictureBoxのCurrentXプロパティとCurrentYプロパティを設定し、それからPrintメソッドを呼び出します。
それに対し、.NETが提供するテキスト描画ツールはもっと強力です。ただし、能力が増大すれば複雑さも増すものです。.NETではテキストに対して驚くほど多様な操作を行えますが、それだけ学ぶべきことも多くあります。まずは単純なテキスト表示から始めて、フォントや位置合わせの変更、クリッピング、折り返し、シェーディング、変形・移動といった新たな技法を少しずつ覚えていけば、しだいに.NETのテキスト表示機能に精通するようになるでしょう。
本稿のサンプルコードには、これらの技法の具体例を示す一連の小さなアプリケーションが含まれています。このコードをそのまま使用することもできますし、これをテンプレートとして使用して、独自のアプリケーションを作成してもよいでしょう。なお、本稿の例ではVB.NETを使用していますが、ダウンロードできるサンプルコードにはVB.NETバージョンとC#バージョンが含まれています。
.NETにおけるテキストの基礎
.NETアプリケーションでテキストを描画するときは、必ずGraphicsオブジェクトが必要になります。このオブジェクトには、テキストはもとより、直線、多角形、曲線、イメージを描画するのに必要なツールが備わっています。テキストはDrawStringメソッドで描画します。
.NETのグラフィックスで素晴らしいことの1つは、Graphicsオブジェクトを使って何にでも描画できることです。描画する対象はフォーム、PictureBox、オーナー描画メニュー、プリンタ、あるいはもっと風変わりなものでもかまいません。つまり、1種類の描画方法を学べば、後はどんなサーフェスへの描画操作も同じように行えるということです。
本稿の例では、フォームのPaintイベントハンドラで描画を行っています。このイベントハンドラのe.Graphicsパラメータが、描画すべきグラフィックスオブジェクトを提供します。
.NETのテキスト描画ツールは複雑かもしれませんが、テキストを最も単純な形で描画するのは難しくありません。例えば、フォームのPaintイベントハンドラの中に次のコードを記述すれば、フォームの左上隅からのピクセル数として指定した点(10, 10)に「BasicText」というテキストを描画できます。テキストはフォームの現在のフォントで描画され、ブルーになります。このコードはVB.NETのものですが、サンプルコードにはVB.NETバージョンとC#バージョンが含まれています。
e.Graphics.DrawString("BasicText", Me.Font, Brushes.Blue, 10, 10)
このコードを使うのは非常に簡単です。デザイン時にフォームのフォントを変更すれば、テキストを好みのフォントで描画できます。ただし、描画するテキストがすべて同じフォントになるという難点があります。
この問題に対処するため、次のような少しだけ複雑なバージョンで、このわずかなテキストのために独自のフォントを用意します。このコードは、24ポイントのボールドのComic Sans MSフォントを作成する、Usingステートメントで始まります。Usingステートメントの後のEnd Usingステートメントに達すると、VB.NETによってそのフォントのDisposeメソッドが自動的に呼び出されます。フォントを作成した後は、先ほどのコードと同じようにDrawStringを呼び出しますが、今度は新しいフォントを渡す点が異なります。
Using the_font As New Font("Comic Sans MS", 24, FontStyle.Bold) e.Graphics.DrawString("BasicText", the_font, _ Brushes.Blue, 10, 20) End Using
図1は、この2つのDrawString呼び出しの結果を示しています。GraphicsオブジェクトのDrawStringメソッドはテキストを描画し、フォームのフォントを使用することも、カスタムフォントを作成することもできます。

DrawStringはキャリッジリターンやタブなどの特殊文字も処理します。図2は前記の例と同じような描画コードの実行結果ですが、この例ではキャリッジリターンとラインフィードの対およびタブを含んだテキストを描画しています。DrawStringはキャリッジリターンとラインフィードの組み合わせで新しい行を開始し、タブで水平スペースを追加します。
VB.NETバージョンでは"The quick brown fox" & vbCrLf & "jumps over" & vbTab & "the lazy dog"という文字列を描画し、C#バージョンでは"The quick brown fox
jumps overthe lazy dog"という文字列を描画します。

テキストの位置合わせ
前記の例では、DrawStringメソッドに描画テキストの左上隅を表すX座標とY座標を渡していましたが(この方法はVisual Basic 6でも有効です)、X座標とY座標を渡す代わりに点(Point)を渡すこともできますし、テキストの描画領域となる矩形領域(Rectangle)を渡すこともできます。
左上隅の座標を渡す方法には、テキストの位置合わせがしにくいという問題があります。例えば、テキストを右揃えにしたい場合、文字列の描画幅がどれだけになるか計算したうえで、右マージンから文字列の幅を差し引いた位置にX座標を設定する必要があります。テキストを中央揃えにしたい場合は、中央の座標から文字列の描画幅の半分を差し引く必要があります。垂直方向の位置合わせでも同様の計算が必要になります。
こうした計算を多くの人たちが長年やってきたわけですが、DrawStringメソッドを使用すればテキストの位置合わせが簡単になります。DrawStringには、StringFormatオブジェクトをパラメータとして受け取るオーバーロードバージョンがいくつもあります。StringFormatオブジェクトのAlignmentプロパティとLineAlignmentプロパティによって、それぞれ文字列の水平方向の位置合わせと垂直方向の位置合わせが決定されます。AlignmentとLineAlignmentの値をNear、Center、またはFarに設定すれば、DrawStringでテキストを基点の左/上、中央、または右/下に配置することができます。
次のコードでは3行のテキストを水平方向に中央揃えしています。まず、これから描くテキストの基点の位置を分かりやすく示すために、ある点のX座標とY座標を定義し、FillEllipseを呼び出して丸印を付けています。次にUsingステートメントでStringFormatオブジェクトを作成し、AlignmentプロパティをCenterに設定して、テキストを水平方向に中央揃えします。そしてLineAlignmentプロパティをNearに設定し、基点の下にテキストを描画します。最後にDrawStringを呼び出して、前記の例で使用したものと同じようなパラメータを指定し、最後のパラメータとしてStringFormatオブジェクトを渡します。この実行結果を図3に示します。
Dim x As Integer = 150 Dim y As Integer = 20 e.Graphics.FillEllipse(Brushes.Red, x - 2, y - 2, 5, 5) Using string_format As New StringFormat() string_format.Alignment = StringAlignment.Center string_format.LineAlignment = StringAlignment.Near e.Graphics.DrawString( _ "Visual Basic" & vbCrLf & _ "Makes string formatting easy" & vbCrLf & _ "C# does also", _ Me.Font, Brushes.Blue, x, y, string_format) End Using

テキストを描画する方法としては、DrawStringにX座標とY座標または点を渡すよりも、むしろフォーマット矩形を渡すほうが直観的かもしれません。この方法ならば、StringFormatオブジェクトのAlignmentプロパティとLineAlignmentプロパティを使って、矩形内でのテキストの位置を指定できます。次のコードは、左上隅が(10, 10)にあり、幅175ピクセル、高さ70ピクセルの矩形の右下隅にテキストを描画する方法を示しています。
layout_rect = New Rectangle(10, 10, 175, 70) string_format.Alignment = StringAlignment.Far string_format.LineAlignment = StringAlignment.Far e.Graphics.DrawString("Far,Far", Me.Font, _ Brushes.Blue, layout_rect, string_format)
ダウンロードサンプル中のプログラム「StringAlignment」に完全なコードが含まれています。図4は、Alignment値とLineAlignment値の9通りの組み合わせによるサンプル文字列の描画結果を示しています。例えば、右上のテキストはAlignment = FarおよびLineAlignment = Nearで描画されたものです。
テキストのクリッピングと折り返し
StringFormatオブジェクトのFormatFlagsプロパティは、DrawStringでテキストを描画する際の、フォーマット矩形内でのテキストの折り返しとクリッピングを制御します。表1に、クリッピングと折り返しの制御に使用できるFormatFlagsの値を示します。
| 値 | 結果 |
| FitBlackBox | 行が通常どおり折り返される。フォーマット矩形の下まで伸びる行はクリッピングされるので、行の一部しか描画されないこともある。 |
| LineLimit | 大部分の行は通常どおり折り返される。テキストに含まれている行が多すぎて入りきらないと、最後の行が文字境界でクリッピングされるので、単語の一部が欠けることがある。 |
| NoClip | 行が通常どおり折り返され、フォーマット矩形の下まで継続される。 |
| NoWrap | 行が折り返されない。フォーマット矩形の端からはみ出す行はクリッピングされる。 |
図5にFormatFlags値の効果を示します。StringFormatオブジェクトのFormatFlagsプロパティによって、テキストのクリッピングと折り返しをどのように行うかが決定されます。

StringFormatオブジェクトのTrimmingプロパティも、DrawStringによるテキストのクリッピングに影響を及ぼします。このプロパティはStringTrimming列挙値を受け取ります。表2に有効なStringTrimming列挙値を示します。
| 値 | 結果 |
| Character | 収容できる最後の文字の後でテキストがトリミングされる。 |
| EllipsisCharacter | 収容できる最後の文字の後でテキストがトリミングされ、末尾に省略記号が追加される。 |
| EllipsisPath | 文字列が収まるように、文字列の中央部のテキストが省略記号に置き換えられる。これはファイルパスの先頭と末端を表示したいときに特に便利である。 |
| EllipsisWord | 収容できる最後の単語の後でテキストがトリミングされ、末尾に省略記号が追加される。 |
| None | テキストがトリミングされない。 |
| Word | 収容できる最後の単語の後でテキストがトリミングされる。 |
例えば次のコードでは、収容できる最後の単語の後でテキストがクリッピングされ、末尾に省略記号が追加されます。
string_format.Trimming = StringTrimming.EllipsisWord
サンプルプログラム「Ellipses」は、それぞれのStringTrimming値の使用例を示しています。図6は、StringFormat.TrimmingをEllipsisCharacterに設定したときの出力例です。StringFormat.Trimmingプロパティを「EllipsisCharacter」に設定すると、トリミングされたテキストが描画され、末尾に省略記号が追加されます。

テキストの変形・移動
DrawStringは変形していない通常のフォントを使ってテキストを描画します。フォントを伸縮したり、回転したり、歪めたりすることはありません。ただし、DrawStringメソッドを提供しているGraphicsオブジェクトには、テキストを含めて、あらゆる描画物を回転したり、移動したり、伸縮したりするためのメソッドも用意されています。GraphicsオブジェクトのTranslateTransform、ScaleTransform、RotateTransformの各メソッドは、描画物の移動、伸縮、回転を行います。
例えば、次のコードでは垂直方向に引き伸ばされたテキストを描画しています。まずフォームの中心(cx, cy)を見つけ、それからScaleTransformメソッドを使用し、水平方向の倍率を1とし(水平方向のサイズは変えない)、垂直方向の倍率を6として描画物を引き伸ばします。
最後に水平方向の距離cxと垂直方向の距離cyでの移動を追加します。こうすると、描画オブジェクトが原点を基準にして移動し、フォームの中央に配置されます。TranslateTransformの最後のパラメータがDrawing2D.MatrixOrder.Appendになっていることに注意してください。これにより、Graphicsオブジェクトが伸縮の後で移動を適用します。既定ではどういうわけか、これらのルーチンは先行する変形・移動があっても自らの処理を先に実行します。しかし、変形・移動は適切な順序で行うことが重要です。一般に、変形・移動が逆の順序で適用されると、結果は同じになりません。
このコードではGraphicsオブジェクトの変形・移動をセットアップした後に、原点に中央揃えされたテキストを描画しています。すると、定義した変形・移動によってテキストが引き伸ばされ、フォームの中心に移動します。実行結果を図7に示します。
Dim cx As Integer = Me.ClientSize.Width 2 Dim cy As Integer = Me.ClientSize.Height 2 e.Graphics.ScaleTransform(1, 6, Drawing2D.MatrixOrder.Append) e.Graphics.TranslateTransform(cx, cy, _ Drawing2D.MatrixOrder.Append) Using string_format As New StringFormat() string_format.Alignment = StringAlignment.Center string_format.LineAlignment = StringAlignment.Center e.Graphics.DrawString("StretchedText", Me.Font, _ Brushes.Blue, 0, 0, string_format) End Using

GraphicsオブジェクトのRotateTransformメソッドは、どんな描画物でも原点を基準にして回転させます。しかし、原点以外の点を基準にして回転させた方が便利なことが多いでしょう。RotateTransformで直接これを行うことはできませんが、他の点を基準にして回転を行うのは難しくありません。まず、その点を原点に移動し、回転を実行してから、その点をまた元の位置に移動すればよいのです。
次のコードに示すRotateAtメソッドは、このプロセスを簡単にするものです。任意の点を基準にして回転できるように、Graphicsオブジェクトを調整しています。
’ Prepare the Graphics object to rotate ’ at the indicated point. Private Sub RotateAt( _ ByVal gr As Graphics, _ ByVal cx As Integer, _ ByVal cy As Integer, _ ByVal angle As Single) gr.ResetTransform() gr.TranslateTransform(-cx, -cy, _ Drawing2D.MatrixOrder.Append) gr.RotateTransform(angle, _ Drawing2D.MatrixOrder.Append) gr.TranslateTransform(cx, cy, _ Drawing2D.MatrixOrder.Append) End Sub
サンプルプログラム「RotatedText」では、このRotateAtメソッドを使って、回転させた月名を格子の上に描画しています(図8を参照)。
テキストのシェーディング
Graphicsオブジェクトには主要なグラフィカルルーチン群が2つあります。図形を塗りつぶすものと描画するものです。例えば、DrawRectangleは矩形の輪郭を描画し、FillRectangleは矩形を塗りつぶします。これらの描画ルーチンは、直線の描画方法を制御するPenオブジェクトをパラメータとして受け取ります。同様に、塗りつぶしルーチンは、領域の塗りつぶし方法を制御するBrushesオブジェクトを受け取ります。
DrawStringメソッドは頭に「Draw」という語が付いていますが、実際にはテキストをブラシで塗りつぶしています。これまで見てきた例では塗りつぶしブラシ(SolidBrush)を使用していましたが、TextureBrush、HatchBrush、LinearGradientBrushといった、少し変わったブラシクラスを使用することもできます。
例えば、次のコードではLinearGradientBrushでシェーディングされたテキストを描画しています。まずフォームの中心を見つけ、StringFormatオブジェクトをセットアップして、テキストを基点に中央揃えします。それからGraphicsオブジェクトのMeasureString関数を使用し、フォームのフォントを使ってGraphicsオブジェクトで描画する際にテキストがどれだけの大きさになるかを調べます。このサイズは、テキストを収容する矩形の四隅の位置を知るために使います。また、垂直方向にグラデーションを付けて矩形を塗りつぶすブラシを作成し、このブラシでテキストを描画します。最後に矩形を描画します。
’ Find the center of the form. Dim origin As New PointF( _ Me.ClientSize.Width 2, _ Me.ClientSize.Height 2) Const TXT As String = "ShadedText" Using string_format As New StringFormat() ’ See how big the text will be using the form’s font. string_format.Alignment = StringAlignment.Center string_format.LineAlignment = StringAlignment.Center Dim txt_size As SizeF = e.Graphics.MeasureString( _ TXT, Me.Font, origin, string_format) ’ Make a brush that shades vertically. Dim rect As New Rectangle( _ origin.X - txt_size.Width 2, _ origin.Y - txt_size.Height 2, _ txt_size.Width, _ txt_size.Height) Using br As New LinearGradientBrush( _ rect, Color.Red, Color.Blue, _ LinearGradientMode.Vertical) e.Graphics.DrawString(TXT, Me.Font, br, _ origin, string_format) End Using ’ br e.Graphics.DrawRectangle(Pens.Red, rect) End Using ’ string_format
図9は結果を示しています。テキストを囲んでいる矩形の縁の周りに余分なスペースがあることに注意してください。
おわりに
Visual Studio .NETには、テキストを描画するための強力なツール群が用意されています。さまざまなフォントで、いろいろな位置にテキストを描画したり、クリッピングや折り返しや位置合わせを制御したり、伸縮や回転によりテキストを変形・移動したり、種々のブラシを使ってテキストをシェーディングしたり塗りつぶしたりすることができます。回転し、伸縮し、適切な位置に配置し、巧みにシェーディングしたテキストを描画するのは、若干手間のかかることかもしれませんが、少し練習すれば、テキスト処理技法を自在に使いこなせるようになります。


