MeasureCharacterRanges により文字列の末尾のスペース文字の情報が取得できない場合がある

 こんにちは、Platform SDK (Windows SDK) サポートチームです。

今回は、System.Drawing.Graphics.MeasureCharacterRanges メソッドにより、文字の描画位置を取得する場合に、正しい値が取得できない問題についてご案内します。

現象

Graphics.MeasureCharacterRanges メソッドにより、「末尾のスペース文字」の位置とサイズを計算した場合、値が 0 になる可能性が確認されています。

以下は MeasureCharacterRanges メソッドの使用例です。

private void Form1_Paint(object sender, PaintEventArgs e)

{

// 計算対象の文字列を定義します

string measureString = "AAAAAAAAAA ";

// CharacterRange 構造体を定義して、計算対象の文字を設定します

CharacterRange[] characterRanges = new CharacterRange[measureString.Length];

for (int i = 0; i < measureString.Length; i++)

{

characterRanges[i] = new CharacterRange(i, 1);

}

// パラメータを定義します

Font stringFont = new Font("MS PGothic", 12.0F);

RectangleF layoutRect = new RectangleF(50, 50, 500, 500);

StringFormat stringFormat = new StringFormat();

stringFormat.FormatFlags = StringFormatFlags.MeasureTrailingSpaces;

stringFormat.SetMeasurableCharacterRanges(characterRanges);

// 計算対象の文字列を描画します

e.Graphics.DrawString(measureString, stringFont, Brushes.Black, 50, 50, stringFormat);

// MeasureCharacterRanges により、各文字の位置を計算します

Region[] stringRegions = e.Graphics.MeasureCharacterRanges(measureString, stringFont, layoutRect, stringFormat);

// MeasureCharacterRanges の計算結果を基に、各文字の描画位置を赤線で囲みます

for (int i = 0; i < measureString.Length; i++)

{

RectangleF measureRect = stringRegions[i].GetBounds(e.Graphics);

e.Graphics.DrawRectangle(new Pen(Color.Red, 1), Rectangle.Round(measureRect));

}

}

上記コードを実行すると、"AAAAAAAAAA " の各文字の位置情報を MeasureCharacterRanges で計算し、各文字を赤線で囲みます。

しかし、「末尾のスペース文字」については、X, Y, Width, Height の全てに 0 が返される可能性があります。

そのため以下のような表示結果になります。

clip_image002

これは、文字位置の計算処理中に丸め誤差が発生することに起因しています。

また、「末尾のスペース文字」の幅が 0 になる影響で、直前の「A」の文字幅がスペース文字の幅だけ大きくなっています。

なお、"AAAAAAAAAA   " のように複数のスペース文字が存在する場合にも、いずれかのスペース文字で、正しい計算結果が取得できないことがあります。

「末尾のスペース文字」を計算するため、MeasureTrailingSpaces フラグを指定して、MeasureCharacterRanges を呼び出す場合には、結果として 0 が返されていないかどうか確認し、返っていた場合には以下の回避方法などにより回避することを検討してください。

回避方法

本問題については、現時点で、修正の予定がありません

また、0 が返されるかどうかは、対象の文字列や利用されるフォントにも依存しますので、事前に判別することができません。

そのため、0 が返された場合には、「末尾のスペース文字を削除した文字列」と「末尾のスペース文字」を分けて、それぞれの計算を行うか、文字数を少なくするなど、パラメータを変更して、再度 MeasureCharacterRanges を実行してください。

例えば、計算対象の文字列から「末尾のスペース文字」を削除し、以下のように定義します。

string measureString = "AAAAAAAAAA";

この場合は以下のような表示結果が得られます。

clip_image004

次に、計算対象の文字列を、以下のように「スペース文字」だけで定義します。

string measureString = " ";

この場合は以下のような表示結果が得られます。

clip_image006

このように文字列を分割すれば正しい文字幅が取得できますので、これらの結果を基にプログラムの処理を行ってください。